Show ComboBox group header for Silverlight

2019-06-23 23:16发布

I want to show a ComboBox with OPTGROUP style header gruopings in Silverlight. Every website I find (including questions on SO) that sovle this link to an outdated link and, handily, show no code snippets for me to work from.

E.g.:

enter image description here

So how do I do this?

3条回答
对你真心纯属浪费
2楼-- · 2019-06-23 23:22

Group header is not supported in ComboBox. Alternate way for to implement it with DataGrid or use TreeView to show grouped data.

However, you can try something like this,

Silverlight Custom ComboBox

查看更多
手持菜刀,她持情操
3楼-- · 2019-06-23 23:24

Here is one generalized approach. It's not bad, and should be flexible enough -- although it has the weakness of requiring an extended collection class (cannot make use of CollectionViewSource).

Grouped ComboBox screenshot

Step 1 ComboBoxGroupHeader object

This plays the role of "dummy entry" mentioned by @NiteshChordiya.

public class ComboBoxGroupHeader
{
    public ComboBoxGroupHeader(object header)
    {
        Header = header;
    }

    public object Header { get; protected set; }
}

Step 2 Extended ComboBox

This overrides PrepareContainerForItemOverride, in order to tinker with the dummy items' containers. It also provides an (optional) "HeaderTemplate".

public class GroupedComboBox : ComboBox
{
    public DataTemplate HeaderTemplate
    {
        get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
        set { SetValue(HeaderTemplateProperty, value); }
    }
    public static readonly DependencyProperty HeaderTemplateProperty =
        DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(GroupedComboBox), new PropertyMetadata(null));

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
        var container = element as ComboBoxItem;
        if (container != null && item is ComboBoxGroupHeader)
        {
            // prevent selection
            container.IsHitTestVisible = false;

            // adjust the container to display the header content
            container.ContentTemplate = HeaderTemplate
            container.Content = ((ComboBoxGroupHeader)item).Header;
        }
    }
}

Step 3 Extended collection class

This does the grouping, and adds the dummy "ComboBoxGroupHeader" entries. This implementation is essentially read-only (groupings would break if you tried to add new items), but it would be straight-forward to support operations like "Add", "Insert", etc.

public class GroupedCollection<T, TGroup> : ObservableCollection<object>
{
    private Func<T, TGroup> _grouping;

    public IEnumerable<T> BaseItems
    {
        get { return base.Items.OfType<T>(); }
    }

    public GroupedCollection(IEnumerable<T> initial, Func<T, TGroup> grouping)
        : base(GetGroupedItems(initial, grouping))
    {
        _grouping = grouping;
    }

    private static IEnumerable<object> GetGroupedItems(IEnumerable<T> items, Func<T, TGroup> grouping)
    {
        return items
            .GroupBy(grouping)
            .SelectMany(grp => 
                new object[] { new ComboBoxGroupHeader(grp.Key) } 
                    .Union(grp.OfType<object>())
            );
    }
}

Usage

It works like a normal ComboBox, with the optional HeaderTemplate:

<local:GroupedComboBox ItemsSource="{Binding Source}">
    <local:GroupedComboBox.HeaderTemplate>
        <TextBlock FontSize="9" TextDecorations="Underline" Foreground="DarkGray"
                   Text="{Binding}" />
    </local:GroupedComboBox.HeaderTemplate>
</local:GroupedComboBox>

For the grouped to take effect, the source must use the "GroupedCollection" above, so as to include the dummy header items. Example:

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

    public Item(string name, string category)
    {
        Name = name;
        Category = category;
    }
}

public class Items : GroupedCollection<Item, string>
{
    public Items(IEnumerable<Item> items)
        : base(items, item => item.Category) { }
}

public class ViewModel
{
    public IEnumerable<Item> Source { get; private set; }

    public ViewModel()
    {
        Source = new Items(new Item[] {
            new Item("Apples", "Fruits"),
            new Item("Carrots", "Vegetables"),
            new Item("Bananas", "Fruits"),
            new Item("Lettuce", "Vegetables"),
            new Item("Oranges", "Fruits")
        });
    }
}
查看更多
聊天终结者
4楼-- · 2019-06-23 23:36

See my similar question: How to show group header for items in Silverlight combobox? I put dummy entries in collection source to indicate the group header and then modified DataTemplate to show the group headers in different way and normal entries in separate way.

查看更多
登录 后发表回答