Home > Uncategorized > WPF listbox grouping problem

WPF listbox grouping problem

I had an interesting (and frustrating) problem with WPF the other day. A colleague of mine was using a simple lisbox to display a bunch of items and the sun was shining and the birds were singing…

A simplified version of the application is given below along with its xaml definition;

clip_image002

<Window x:Class="ListboxGrouping.SampleWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="SampleWindow" Height="300" Width="300">
    <Window.Resources>
        <CollectionViewSource x:Key="MyCVS" Source="{Binding MyItems}">
 
        </CollectionViewSource>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding Source={StaticResource MyCVS}}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="5">
                        <TextBlock Text="{Binding Name}" Margin="5"/>
                        <TextBlock Text="{Binding Status}" Margin="5"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>
 

The problem arose when my colleague started to try and group them by adding a group description and a group style: 

<Window x:Class="ListboxGrouping.SampleWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="SampleWindow" Height="300" Width="300">
    <Window.Resources>
        <CollectionViewSource x:Key="MyCVS" Source="{Binding MyItems}">
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="Status"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding Source={StaticResource MyCVS}}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="5">
                        <TextBlock Text="{Binding Name}" Margin="5"/>
                        <TextBlock Text="{Binding Status}" Margin="5"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </ListBox.GroupStyle>
        </ListBox>
    </Grid>
</Window>
 

The result is displayed below (and it was not satisfactory…)

clip_image002[4]

Only the headers showed up and no items! What was going on? First I though we had some bug in our code since the real application had a lot of other things going on behind the scenes but after going through it for quite a few times I could not find anything strange. So, I extracted the code into a simple test application and, to my great disbelieves, the test application worked like a charm. Darn, something was lurking in the real application and I could not find it. I tried to google for other people having the same problem but no luck. After poking around for quite a while (including a lot of cursing, swearing and knocking my head against the wall) I finally realized that the real application was styled. The listbox was not really plain vanilla (god damn it!) – it was styled! After realizing this, the solution was just work. I extracted the standard control template for listbox by using the following helper code:

public static void Dump(Control ctrl)
{
    StringBuilder sb = new StringBuilder();
    using (TextWriter writer = new StringWriter(sb))
    {
        System.Windows.Markup.XamlWriter.Save(ctrl.Template, writer);
    }
    Debug.WriteLine(sb.ToString());
}

Then I could compare the standard listbox template to the template we were using. Our template looked something like below (which is very similar to a sample listbox template given on MSDN which also has the same problem):

<Style TargetType="{x:Type ListBox}">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="Background" Value="{DynamicResource DarkWindowBackgroundBrush}"/>
    <Setter Property="BorderBrush" Value="{DynamicResource DarkSolidBorderBrush}"/>
    <Setter Property="Foreground" Value="{DynamicResource Silver}"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBox}">
                <Grid>
                    <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
                    <ScrollViewer Margin="1" Focusable="false" Background="{TemplateBinding Background}">
 
                        <!-- The StackPanel is used to display the children by setting IsItemsHost to be True -->
                        <StackPanel Margin="2" IsItemsHost="true"/>
 
                    </ScrollViewer>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" Value="{DynamicResource DarkDisabledBackgroundBrush}" TargetName="Border"/>
                        <Setter Property="BorderBrush" Value="{DynamicResource DarkDisabledBorderBrush}" TargetName="Border"/>
                    </Trigger>
                    <Trigger Property="IsGrouping" Value="true">
                        <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
 

I tried to mark the problem in bold letters. The custom template was using a simple stackpanel to display the items which worked fine as long as grouping was not enabled. The standard template, however, used an ItemsPresenter instead. Changing this closed the deal:

clip_image002[6]

Sweet! Wpf styling is powerful and like all powerful tools it can both rock your world and help you to shoot yourself in the foot.

Advertisements
Categories: Uncategorized
  1. November 19, 2013 at 11:46 pm

    My problem exactly. Thank you ever so much.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: