Grouping in WPF CheckedListBox

18 Nov 201817 minutes to read

By default, the WPF CheckedListBox items are displayed in a listed view. You can group the items by adding a group description to the CollectionView.GroupDescriptions collection.

The selection state of the group header varies based on the checked or unchecked state of the child items. Groups can be expanded or collapsed, and the child items in a group can be checked or unchecked based on the user’s perspective.

//Model.cs
class Vegetable {
    public string Category { get; set; }
    public int Price { get; set; }
    public string Name { get; set; }
}

//ViewModel.cs
class ViewModel {
    public ObservableCollection<Vegetable> Vegetables { get; set; }
    public ICommand LoadedCommand { get; set; }
    public void OnLoaded(object param) {
        CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(Vegetables);
       
        //Adding group description
            view.GroupDescriptions.Add(new PropertyGroupDescription("Price"));
    }
    public ViewModel() {
        Vegetables = new ObservableCollection<Vegetable>();
        Vegetables.Add(new Vegetable { Price=10, Name="Yarrow", Category="Leafy and Salad"});
        Vegetables.Add(new Vegetable { Price=20, Name="Cabbage", Category="Leafy and Salad"});
        Vegetables.Add(new Vegetable { Price=30, Name="Horse gram", Category="Beans"});
        Vegetables.Add(new Vegetable { Price=20, Name="Green bean", Category="Beans"});
        Vegetables.Add(new Vegetable { Price=10, Name="Onion", Category="Bulb and Stem"});
        Vegetables.Add(new Vegetable { Price=30, Name="Nopal", Category="Bulb and Stem"});

        //Initialize the CheckListBox LoadedCommand
        LoadedCommand = new DelegateCommand<object>(OnLoaded);
    }
}
<syncfusion:CheckListBox ItemsSource="{Binding Vegetables}" DisplayMemberPath="Name"
                         Name="checkListBox">
    <syncfusion:CheckListBox.DataContext>
        <local:ViewModel></local:ViewModel>
    </syncfusion:CheckListBox.DataContext>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</syncfusion:CheckListBox>

Here, the Vegetables items are grouped based on their price.

CheckListBox items grouped by the Price property

Click here to download the sample that showcases the grouping support in the WPF CheckedListBox.

Nested Grouping

We can create multi-level nested groups for the WPF CheckedListBox items by adding two or more group descriptions to the CollectionView.GroupDescriptions collection. Child level groups are created in the order of the group descriptions added to the collection.

//Model.cs
class Vegetable {
    public string Category { get; set; }
    public int Price { get; set; }
    public string Name { get; set; }
}

//ViewModel.cs
class ViewModel {
    public ObservableCollection<Vegetable> Vegetables { get; set; }
    public ICommand LoadedCommand { get; set; }
    public void OnLoaded(object param) {
        CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(Vegetables);
       
        //Adding the multiple group description
            view.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
            view.GroupDescriptions.Add(new PropertyGroupDescription("Price"));
    }
    public ViewModel() {
        Vegetables = new ObservableCollection<Vegetable>();
        Vegetables.Add(new Vegetable { Price=10, Name="Yarrow", Category="Leafy and Salad"});
        Vegetables.Add(new Vegetable { Price=20, Name="Pumpkins", Category="Leafy and Salad"});
        Vegetables.Add(new Vegetable { Price=30, Name="Cabbage", Category="Leafy and Salad"});
        Vegetables.Add(new Vegetable { Price=10, Name="Spinach", Category="Leafy and Salad"});
        Vegetables.Add(new Vegetable { Price=20, Name="Wheat Grass", Category="Leafy and Salad"});
        Vegetables.Add(new Vegetable { Price=10, Name="Horse gram", Category="Beans"});
        Vegetables.Add(new Vegetable { Price=20, Name="Chickpea", Category="Beans"});
        Vegetables.Add(new Vegetable { Price=30, Name="Green bean", Category="Beans"});
        Vegetables.Add(new Vegetable { Price=10, Name="Garlic", Category="Bulb and Stem"});
        Vegetables.Add(new Vegetable { Price=20, Name="Onion", Category="Bulb and Stem"});
        Vegetables.Add(new Vegetable { Price=30, Name="Nopal", Category="Bulb and Stem"});

        //Initialize the checklistbox LoadedCommand
        LoadedCommand = new DelegateCommand<object>(OnLoaded);
    }
}
<syncfusion:CheckListBox ItemsSource="{Binding Vegetables}" DisplayMemberPath="Name"
                         Name="checkListBox">
    <syncfusion:CheckListBox.DataContext>
        <local:ViewModel></local:ViewModel>
    </syncfusion:CheckListBox.DataContext>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</syncfusion:CheckListBox>

Here, the Vegetables items are grouped based on their Category and then, the categorized properties are grouped by price.

CheckListBox items with nested grouping

Click here to download the sample that showcases the nested grouping support in the WPF CheckedListBox.

Custom grouping

WPF CheckedListBox allows you to group the items based on custom logic when the common grouping functionality does not meet your requirement. This can be achieved by using the IValueConverter.

//Model.cs
public class Employee {
    public string Name { get; set; }
    public int Age { get; set; }
    public DateTime DOB { get; set; }
}

//EmployeesCollection.cs
public class EmployeesCollection : ObservableCollection<Employee> {
}


//ViewModel.cs
public class ViewModel : NotificationObject {
    private ObservableCollection<GroupDescription> groupDescriptions;
    private EmployeesCollection employeesCollection;

    public ObservableCollection<GroupDescription> GroupDescriptions {
        get {
            return groupDescriptions;
        }
        set {
            groupDescriptions = value;
            RaisePropertyChanged("GroupDescriptions");
        }
    }

    public EmployeesCollection EmployeesCollection {
        get {
            return employeesCollection;
        }
        set {
            employeesCollection = value;
            RaisePropertyChanged("EmployeesCollection");
        }
    }

    public ViewModel() {
        EmployeesCollection = new EmployeesCollection();
        GroupDescriptions = new ObservableCollection<GroupDescription>();
    }
}
//Converter for the Custom Grouping 
public class DateConverter : IValueConverter {
    public string Category { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        DateTime result;
        string MyString = value.ToString();
        DateTime.TryParse(MyString, out result);

        if (Category == "Year") {
            //Grouping the employee who are all born on 1980-1990 
            if ((DateTime.Compare(new DateTime(1980, 01, 01), result)) <= 0 &&
                (DateTime.Compare(new DateTime(1990, 12, 31), result)) >= 0)
            {
                return "Birth Year(1980 - 1990)";
            }
            //Grouping the employee who are all born on 1990-2000 
            else if ((DateTime.Compare(new DateTime(1991, 01, 01), result)) <= 0 &&
                (DateTime.Compare(new DateTime(2000, 12, 31), result)) >= 0)
            {
                return "Birth Year(1991 - 2000)";
            }
        }
        else if (Category == "Month") {
            //Sub-Grouping the employee who are all born on Jan-Mar 
            if (result.Month >= 1 && result.Month <= 3) {
                return "Quarter-1";
            }
            //Sub-Grouping the employee who are all born on Apr-Jun
            else if (result.Month >= 4 && result.Month <= 6) {
                return "Quarter-2";
            }
            //Sub-Grouping the employee who are all born on Jul-Sep
            else if (result.Month >= 7 && result.Month <= 9) {
                return "Quarter-3";
            }
            //Sub-Grouping the employee who are all born on Oct-Dec
            else {
                return "Quarter-4";
            }
        }
        return "Others";
    }
   
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        throw new NotImplementedException();
    }
}
<Window.Resources>
    <!-- Define employee details-->
    <local:EmployeesCollection x:Key="employees">
        <local:Employee Name="Daniel" DOB="02/15/1983" Age="37"/>
        <local:Employee Name="James" DOB="06/29/1988" Age="32"/>
        <local:Employee Name="Michael" DOB="06/10/1987" Age="33"/>
        <local:Employee Name="Daniel" DOB="08/23/1980" Age="40"/>
        <local:Employee Name="John" DOB="12/22/1990" Age="30"/>
        <local:Employee Name="Paul" DOB="04/08/1997" Age="23"/>
        <local:Employee Name="Mark" DOB="08/05/1994" Age="26"/>
        <local:Employee Name="George" DOB="10/01/1998" Age="22"/>
        <local:Employee Name="Daniel" DOB="10/01/1998" Age="24"/>
        <local:Employee Name="Thomes" DOB="10/08/1995" Age="25"/>
        <local:Employee Name="Steven" DOB="12/15/1982" Age="38"/>
    </local:EmployeesCollection>
    
    <!-- Define view model with group description -->
    <local:DateConverter Category="Year" x:Key="year"/>
    <local:DateConverter Category="Month"  x:Key="month"/>
    <local:ViewModel x:Key="viewModel">
        <local:ViewModel.GroupDescriptions>
            <PropertyGroupDescription PropertyName="DOB" 
                                      Converter="{StaticResource year}"/>
            <PropertyGroupDescription PropertyName="DOB"
                                      Converter="{StaticResource month}"/>
        </local:ViewModel.GroupDescriptions>
    </local:ViewModel>
</Window.Resources>
<Grid>
    <syncfusion:CheckListBox ItemsSource="{StaticResource ResourceKey=employees}"
                             GroupDescriptions="{Binding GroupDescriptions}"
                             DataContext="{StaticResource viewModel}"
                             DisplayMemberPath="Name"
                             Name="checkListBox"
                             Margin="20"/>
</Grid>

CheckListBox items with custom grouping

Click here to download the sample that showcases the custom grouping support.