Sunday, October 17, 2010

WPF - Expander in ItemsControl (ListBox)

ItemsControl uses VirtualizingStackPanel as Panel of its item. This induces virtual behavior for its item. The elements which are not visible will not be loaded. This provides good performance of WPF applications. The problem is that VirualizingStackPanel introduces issues for resizing when the size of ItemsControl is expected to be reduced because some items are deleted or the size is decreased because some items are collapsed. Because of VirtualizingStackPanel, ItemsControl maintains its size. This would occupy the same space even if items are collapsed which is kind of unexpected sometimes.

Let us see that in an example. Let's create a ListBox with Expander as ListBoxItems. To keep track of actual size of some of the controls in this example, we create TextBlocks to note down their sizes.
<StackPanel>
<TextBlock Text="{Binding ElementName=lstBox, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tst, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tstLBI, Path=ActualHeight}" />
<Border BorderThickness="1"
BorderBrush="Black"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<ListBox x:Name="lstBox">
<ListBox.Items>
<ListBoxItem x:Name="tstLBI">
<Expander Header="Child1" x:Name="tst"
HorizontalAlignment="Left" Height="auto" Width="auto">
<Canvas Background="Blue" Height="50" Width="100" >
<Border Background="Blue"/>
</Canvas>
</Expander>
</ListBoxItem>
<Expander Header="Child2"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
<Expander Header="Child3"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
</ListBox.Items>
</ListBox>
</Border>
</StackPanel>



Now we expand the expander controls in the ListBox as follows:



Now we collapse all Expander controls. As you can see that ListBox still has the same size as all items are still expanded. You can verify that TextBlock is also showing ListBox size as 223.



This is because ListBox uses VirtualizingStackPanel as its ItemsPanel. If we change that to StackPanel as follows, we can fix this.
<StackPanel>
<TextBlock Text="{Binding ElementName=lstBox, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tst, Path=ActualHeight}" />
<TextBlock Text="{Binding ElementName=tstLBI, Path=ActualHeight}" />
<Border BorderThickness="1"
BorderBrush="Black"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<ListBox x:Name="lstBox">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Items>
<ListBoxItem x:Name="tstLBI">
<Expander Header="Child" x:Name="tst"
HorizontalAlignment="Left" Height="auto" Width="auto">
<Canvas Background="Blue" Height="50" Width="100" >
<Border Background="Blue"/>
</Canvas>
</Expander>
</ListBoxItem>
<Expander Header="Child"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
<Expander Header="Child"
HorizontalAlignment="Left">
<Canvas Height="50"
Width="100"
Background="Blue" />
</Expander>
</ListBox.Items>
</ListBox>
</Border>
</StackPanel>

Now when you run the application, this issue should be fixed.

No comments: