Nish26
Nish26

Reputation: 987

Nested grouping with CollectionViewSource in WPF

I have a List Components which is bound to a ListBox in xaml. In the view model, i am trying to use CollectionViewSource as follows :

var groupedItems = CollectionViewSource.GetDefaultView(Components);
groupedItems.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
groupedItems.GroupDescriptions.Add(new PropertyGroupDescription("SubCategory"));

Some of the object (T) in Components collection can have empty SubCategory. Now, when the UI is rendered, an empty header appears for grouping for objects which have empty SubCategory as shown below :

Category
   --SubCategory
      --Item 1
      --Item 2
   --              
      --Item 3
      --Item 4
Category
   ......continues

I would like Item 3 & Item 4 which have no subcategory to appear as immediate child of Category as follows :

 Category
       --Item 3
       --Item 4
       --SubCategory
          --Item 1
          --Item 2     

    Category
       ......continues

Please guide on how to achieve this.

Regards,

Nish26

Upvotes: 1

Views: 2113

Answers (1)

Vitali
Vitali

Reputation: 655

This answer describes approach based on style selectors. Perhaps, there are better ways to achieve similar results, I'd look on them with pleasure.

Style selector

Style selectors allows to apply a style based on custom logic. The following selector should check whether the Name of CollectionViewGroup is null and return corresponding style:

public class GroupStyleSelector : StyleSelector
{
    public Style NoGroupHeaderStyle { get; set; }
    public Style DefaultGroupStyle { get; set; }

    public override Style SelectStyle(object item, DependencyObject container)
    {
        var group = item as CollectionViewGroup;
        if (group?.Name == null /* or any other condition */)
        {
            return NoGroupHeaderStyle;
        }
        return DefaultGroupStyle;
    }
}

Worth mentioning that if the style is null, the default one will be used. Hence, it is important to set NoGroupHeaderStyle.

XAML

A style for the GroupItem that should not show group header:

<Style TargetType="{x:Type GroupItem}" x:Key="NoGroupHeaderStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type GroupItem}">
                <ItemsPresenter />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The default style for GroupItem with header. It's optional.

<Style TargetType="{x:Type GroupItem}" x:Key="DefaultGroupHeaderStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type GroupItem}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <ContentPresenter Grid.Row="0" />
                    <ItemsPresenter Grid.Row="1" Margin="20,0,0,0" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The style selector that was described above:

<local:GroupStyleSelector x:Key="GroupStyleSelector" 
                          NoGroupHeaderStyle="{StaticResource NoGroupHeaderStyle}" 
                          DefaultGroupStyle="{StaticResource DefaultGroupHeaderStyle}" />

Due to the custom style of GroupItem, which is provided by style selector, the group header will not appear for those items, whose SubCategory is null:

<ListBox ItemsSource="{Binding GroupedItems}" DisplayMemberPath="ComponentName">
    <ListBox.GroupStyle>
        <GroupStyle ContainerStyleSelector="{StaticResource GroupStyleSelector}">
            <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <!-- Display the name of CollectionViewGroup -->
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
        </GroupStyle>
    </ListBox.GroupStyle>
</ListBox>

Sorting

In order to show items with empty SubCategory first, they should be sorted by their SubCategory:

CollectionView.SortDescriptions.Add(
    new SortDescription(nameof(Component.SubCategory),
    ListSortDirection.Ascending));

Component

The following class was used as an element in the collection:

public class Component
{
    public string ComponentName { get; set; }
    public string Category { get; set; }
    public string SubCategory { get; set; }
}

Upvotes: 2

Related Questions