在 DropDownButton 中组织项目?

Organize Items in DropDownButton?

我在 ObservableCollection 中有 collection 个项目,每个项目都有一个特定的国家名称(这只是一个字符串)。这是我的 collection:

private ObservableCollection<League> _leagues = new ObservableCollection<League>();
    public ObservableCollection<League> Leagues
    {
        get
        {
            return _leagues;
        }
        set
        {
            _leagues = value;
            OnPropertyChanged();
        }
    }

League 模型只有一个 Name 和一个 NationName 属性。 Xaml 看起来像这样:

<Controls:DropDownButton Content="Leagues" x:Name="LeagueMenu"
                             ItemsSource="{Binding Leagues}"
                                     ItemTemplate="{StaticResource CombinedTemplate}" >
        <Controls:DropDownButton.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding NationName}" />
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </Controls:DropDownButton.GroupStyle>
</Controls:DropDownButton>

但我没有得到 NationName 属性 的任何 header,DropDown 中的项目没有 header 而是作为列表组织的,所以没有组织。 我正在尝试获取 this predisposition.

我做错了什么?

如果您查看链接到的其他 post,答案就是一切 - 特别是您需要绑定到 Collection 视图,而不是直接绑定到 collection。然后您可以在 Collection 视图上设置分组。

因此,在您的情况下,定义 属性:

public ICollectionView LeaguesView { get; private set; }

然后在您创建联赛后 Collection,将视图附加到您的 collection,然后在视图上设置分组:

LeaguesView = (ListCollectionView)CollectionViewSource.GetDefaultView(Leagues);
LeaguesView.GroupDesriptions.Add(new PropertyGroupDescription("NationName"));

然后,将您的 DropDownButton ItemSource 绑定到 LeaguesView,并将您的 HeaderTemplate 更改为绑定到 "Name" - 这是组的名称:

            <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
            </GroupStyle.HeaderTemplate>

如果您想显示组中有多少项目,您也可以在其中使用 ItemCount 属性。

预赛

在 WPF(DropDownButton 的派生)中对 ItemsControl 中的项目进行分组非常简单,分两步完成。首先,您需要通过调整与源集合关联的 ICollectionView 来设置项目源。然后您需要使用至少一个 GroupStyle 项目填充 ItemsControl.GroupStyle 集合 - 否则这些项目将以普通(非分组)方式呈现。

诊断

您面临的主要问题是让下拉菜单以分组方式显示项目。不幸的是,与设置项目源不同,在 DropDownButton 控件的情况下,这不是一件容易完成的事情。其原因源于控件(或更准确地说,它的模板)的设计方式 - 下拉列表显示在 ContextMenu 内,附加到作为模板一部分的 Button(参见 MahApps.Metro source code)。现在 ContextMenu 也派生自 ItemsControl,并且其大部分属性都绑定到模板化 DropDownButton 的相应属性。然而,它的 GroupStyle 属性 并非如此,因为它是只读的非依赖项 属性,无法绑定或设置事件样式。这意味着即使您将项目添加到 DropDownButton.GroupStyle 集合,ContextMenu.GroupStyle 集合仍为空,因此项目以非分组方式显示。

解决方案(解决方法)

最可靠但最麻烦的解决方案是重新模板化控件并将 GroupStyle 项直接添加到 ContextMenu.GroupStyle 集合。但我可以为您提供更简洁的解决方法。

首先,让我们来处理第一步——设置项目源。最简单的方法(在我看来)是在 XAML 中使用 CollectionViewSource。在你的情况下,它会归结为以下几行:

<mah:DropDownButton>
    <mah:DropDownButton.Resources>
        <CollectionViewSource x:Key="LeaguesViewSource" Source="{Binding Leagues}">
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="NationName" />
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </mah:DropDownButton.Resources>
    <mah:DropDownButton.ItemsSource>
        <Binding Source="{StaticResource LeaguesViewSource}" />
    </mah:DropDownButton.ItemsSource>
</mah:DropDownButton>

现在是主要部分 - 我们的想法是创建一个助手 class,它将包含一个附加的依赖项 属性,它将所有者 DropDownButton 控制权分配给ContextMenu 负责展示其项目。更改所有者后,我们将观察其 DropDownButton.GroupStyle 集合并使用 ContextMenu.GroupStyleSelectorContextMenu 提供来自其所有者集合的项目。这是代码:

public static class DropDownButtonHelper
{
    public static readonly DependencyProperty OwnerProperty =
        DependencyProperty.RegisterAttached("Owner", typeof(DropDownButton), typeof(DropDownButtonHelper), new PropertyMetadata(OwnerChanged));

    public static DropDownButton GetOwner(ContextMenu menu)
    {
        return (DropDownButton)menu.GetValue(OwnerProperty);
    }

    public static void SetOwner(ContextMenu menu, DropDownButton value)
    {
        menu.SetValue(OwnerProperty, value);
    }

    private static void OwnerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var menu = (ContextMenu)d;
        if (e.OldValue != null)
            //unsubscribe from the old owner
            ((DropDownButton)e.OldValue).GroupStyle.CollectionChanged -= menu.OwnerGroupStyleChanged;
        if (e.NewValue != null)
        {
            var button = (DropDownButton)e.NewValue;
            //subscribe to new owner
            button.GroupStyle.CollectionChanged += menu.OwnerGroupStyleChanged;
            menu.GroupStyleSelector = button.SelectGroupStyle;
        }
        else
            menu.GroupStyleSelector = null;
    }

    private static void OwnerGroupStyleChanged(this ContextMenu menu, object sender, NotifyCollectionChangedEventArgs e)
    {
        //this method is invoked whenever owners GroupStyle collection is modified,
        //so we need to update the GroupStyleSelector
        menu.GroupStyleSelector = GetOwner(menu).SelectGroupStyle;
    }

    private static GroupStyle SelectGroupStyle(this DropDownButton button, CollectionViewGroup group, int level)
    {
        //we select a proper GroupStyle from the owner's GroupStyle collection
        var index = Math.Min(level, button.GroupStyle.Count - 1);
        return button.GroupStyle.Any() ? button.GroupStyle[index] : null;
    }
}

为了完成第二步,我们需要为 ContextMenu 绑定 Owner 属性(我们将使用 DropDownButton.MenuStyle 来完成)并添加一些 GroupStyle 项目到 DropDownButton:

<mah:DropDownButton>
    <mah:DropDownButton.MenuStyle>
        <Style TargetType="ContextMenu" BasedOn="{StaticResource {x:Type ContextMenu}}">
            <Setter Property="local:DropDownButtonHelper.Owner" Value="{Binding RelativeSource={RelativeSource TemplatedParent}}" />
        </Style>
    </mah:DropDownButton.MenuStyle>
    <mah:DropDownButton.GroupStyle>
        <GroupStyle />
    </mah:DropDownButton.GroupStyle>
</mah:DropDownButton>

我认为这应该足以实现您的目标。