预赛
在 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>
现在主要部分 - 我们将创建一个辅助类,该类将包含一个附加的依赖属性,该属性将所有者 DropDownButton 控件分配给负责呈现其项目的 ContextMenu。更改所有者后,我们将观察其DropDownButton.GroupStyle 集合并使用ContextMenu.GroupStyleSelector 为ContextMenu 提供来自其所有者集合的项目。代码如下:
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>
我认为这应该足以实现您的目标。