Grouping items in a ComboBox
Asked Answered
A

2

49

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]
Auditor answered 27/8, 2010 at 14:15 Comment(2)
Sounds more like a TreeView to me.Earache
I agree... I made a treeview first but the end user wants a combobox...Auditor
F
76

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; }
    }

}
Fang answered 27/8, 2010 at 14:37 Comment(8)
Thank you for helping us with this! Helped a lot! God bless.Whopper
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 ? thxKleon
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 problemComminate
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.Comminate
This answer seems to does not work in UWP. Is there a solution to do the same in a Windows 10 app? ThanksMasao
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.Saltzman
Excellent answer. Binding ItemSource to an ObservableCollection<Item> would be the cherry on the cake :)Laughter
I guess there is no way for the groups to be expandable, like in context menu?Oleviaolfaction
T
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>
Tiddly answered 21/4, 2023 at 2:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.