48

I have a ListView that contains two types of objects, single and multiple. The single is a ordinary TextBlock while the multiple is a ComboBox with items.

I'm trying to group the items in the ComboBox without success. Is it possible? Or should I go for a different approach?

What I'm trying to achieve:

[ComboBox v]
    [Header  ]
    [    Item]
    [    Item]
    [Header  ]
    [    Item]
wonea
  • 4,783
  • 17
  • 86
  • 139
debe
  • 608
  • 1
  • 5
  • 13

2 Answers2

75

It is possible. Use a ListCollectionView with a GroupDescription as the ItemsSource and just provide a GroupStyle to your ComboBox. See sample below:

XAML:

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:StackOverflow"
        xmlns:uc="clr-namespace:StackOverflow.UserControls"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <ComboBox x:Name="comboBox">
            <ComboBox.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </ComboBox.GroupStyle>
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </StackPanel>
</Window>

Code-behind:

namespace StackOverflow
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
            //this.comboBox.DataContext = this;

            List<Item> items = new List<Item>();
            items.Add(new Item() { Name = "Item1", Category = "A" });
            items.Add(new Item() { Name = "Item2", Category = "A" });
            items.Add(new Item() { Name = "Item3", Category = "A" });
            items.Add(new Item() { Name = "Item4", Category = "B" });
            items.Add(new Item() { Name = "Item5", Category = "B" });

            ListCollectionView lcv = new ListCollectionView(items);
            lcv.GroupDescriptions.Add(new PropertyGroupDescription("Category"));

            this.comboBox.ItemsSource = lcv;
        }


    }

    public class Item
    {
        public string Name { get; set; }
        public string Category { get; set; }
    }

}
Community
  • 1
  • 1
ASanch
  • 10,233
  • 46
  • 32
  • 2
    Thank you for helping us with this! Helped a lot! God bless. – Tony Jan 13 '12 at 23:37
  • I tried your solution and it's works. But I can't get the selected value, I tried with combobox.SelectedItem.ToString() but it doesn't return the expected result. Do you have an idea ? thx – Bluety Jul 15 '15 at 13:04
  • 1
    Sorry for the two year delay Bluety, but I had a similar problem because my underlying property (string), didn't match the SelectedItem type (my Item class) and the binding never took place. Binding to SelectedValue instead of SelectedItem solved the problem – Sean Mar 09 '17 at 16:07
  • Actually, binding to SelectedValue instead of SelectedItem can solve the problem but won't carry the group information. Changing the bound property type to the Item class fixed it. – Sean Mar 09 '17 at 16:20
  • This answer seems to does not work in UWP. Is there a solution to do the same in a Windows 10 app? Thanks – Samuel LIOULT Apr 24 '17 at 15:24
  • 6
    This is excellent. But I found it a bit confusing because the Header Template and Item Template both bind to a property called "Name". It'd be clearer if the Item.Name property was renamed to (say) Item.Description, and then in the XAML the Header Template would bind to the implicit "Name" property in the ListCollectionView, and the Item Template would bind to "Description". Great example nonetheless - thanks. – Richard Moore Jan 18 '18 at 15:57
  • 1
    Excellent answer. Binding ItemSource to an ObservableCollection would be the cherry on the cake :) – M Stoerzel May 11 '20 at 09:42
  • I guess there is no way for the groups to be expandable, like in context menu? – Pawcio Nov 10 '21 at 12:26
0

Here is an improvement on ASanch answer to make it MVVM friendly, where you bind to the CollectionView.

View Model:


namespace StackOverflow
{
    public class MainViewModel : INotifyPropertyChanged
    {

        public ObservableCollection<Item> Items { get; set; }
        public CollectionViewSource CollectionView { get; set; }


        public MainViewModel()
        {
            
            List<Item> items = new List<Item>();
            items.Add(new Item() { Name = "Item1", Category = "A" });
            items.Add(new Item() { Name = "Item2", Category = "A" });
            items.Add(new Item() { Name = "Item3", Category = "A" });
            items.Add(new Item() { Name = "Item4", Category = "B" });
            items.Add(new Item() { Name = "Item5", Category = "B" });            
            
            Items = new ObservableCollection<Item>(items);

            var view = new CollectionViewSource();
            view.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
            view.Source = Items;
            CollectionView = view;
        }
        
        public CollectionViewSource CollectionView { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

    }
    
    public class Item
    {
        public string Name { get; set; }
        public string Category { get; set; }
    }

}

XAML:

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:StackOverflow"
        xmlns:uc="clr-namespace:StackOverflow.UserControls"
        Title="MainWindow" Height="350" Width="525">
    
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <StackPanel>
        <ComboBox ItemsSource="{Binding CollectionView.View}" DisplayMemberPath="Name">
            <ComboBox.GroupStyle>
                <GroupStyle/>
            </ComboBox.GroupStyle>
        </ComboBox>
    </StackPanel>
</Window>