在 DropDownButton 中组织项目? [英] Organize Items in DropDownButton?
问题描述
我在 ObservableCollection
中有一个项目集合,每个项目都有一个特定的国家名称(这只是一个字符串).这是我的收藏:
I've a collection of items inside an ObservableCollection
, each item have a specific nation name (that's only a string). This is my collection:
private ObservableCollection<League> _leagues = new ObservableCollection<League>();
public ObservableCollection<League> Leagues
{
get
{
return _leagues;
}
set
{
_leagues = value;
OnPropertyChanged();
}
}
League
模型只有一个 Name
和一个 NationName
属性.Xaml
看起来像这样:
the League
model have only a Name
and a NationName
properties.
The Xaml
looks like this:
<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
属性的任何标题,DropDown
中的项目没有标题而是作为列表组织,因此没有组织.我正在尝试获得这种倾向.
but I doesn't get any header for the NationName
property, the items inside the DropDown
are organized without header but as list, so without organization.
I'm trying to get this predisposition.
我做错了什么?
推荐答案
Preliminaries
在 WPF(DropDownButton
派生自)的 ItemsControl
中对项目进行分组相当简单,分两步完成.首先,您需要通过调整与源集合关联的 ICollectionView
来设置项目源.然后,您需要使用至少一个 GroupStyle
项目填充 ItemsControl.GroupStyle
集合 - 否则这些项目将以普通(非分组)方式呈现.
Preliminaries
Grouping items in an ItemsControl
in WPF (which DropDownButton
derives from) is fairly simple, and is accomplished in two steps. First you need to set up the items source by tweaking an ICollectionView
associated with the source collection. Then you need to populate the ItemsControl.GroupStyle
collection with at least one GroupStyle
item - otherwise the items are presented in a plain (non-grouped) manner.
您面临的主要问题是让下拉菜单以分组方式显示项目.不幸的是,与设置项目源不同,在 DropDownButton
控件的情况下,这不是一件容易完成的事情.其原因源于控件(或更准确地说,其模板)的设计方式 - 下拉列表显示在附加到 Button
的 ContextMenu
中,其中是模板的一部分(参见 MahApps.Metro 源代码).现在ContextMenu
也继承自ItemsControl
,它的大部分属性都绑定到模板化的DropDownButton
的对应属性上.然而,它的 GroupStyle
属性不是这种情况,因为它是一个只读的非依赖属性,不能绑定或事件样式.这意味着即使您将项目添加到 DropDownButton.GroupStyle
集合,ContextMenu.GroupStyle
集合仍为空,因此项目以非分组方式呈现.
The main issue you're facing is getting the drop-down to present the items in a grouped manner. Unfortunately, unlike setting up the items source, it is not something that is easily accomplished in case of the DropDownButton
control. The reason for that stems from the way the control (or, more precisely, its template) is designed - the drop-down is presented inside a ContextMenu
attached to a Button
which is part of the template (see MahApps.Metro source code). Now ContextMenu
also derives from ItemsControl
, and most of its properties are bound to corresponding properties of the templated DropDownButton
. That is however not the case for its GroupStyle
property, because it's a read-only non-dependency property, and cannot be bound or event styled. That means that even if you add items to DropDownButton.GroupStyle
collection, the ContextMenu.GroupStyle
collection remains empty, hence the items are presented in non-grouped manner.
最可靠但最麻烦的解决方案是重新模板化控件并将GroupStyle
项直接添加到ContextMenu.GroupStyle
集合.但我可以为您提供一个更简洁的解决方法.
The most reliable, yet most cumbersome solution would be to re-template the control and add GroupStyle
items directly to the ContextMenu.GroupStyle
collection. But I can offer you a much more concise workaround.
首先,让我们处理第一步 - 设置项目源.最简单的方法(在我看来)是在 XAML 中使用 CollectionViewSource
.在你的情况下,它会归结为以下几点:
First of all, let's deal with the first step - setting up the items source. The easiest way (in my opinion) is to use CollectionViewSource
in XAML. In your case it would boil down to something along these lines:
<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
提供来自其所有者集合的项目.代码如下:
Now for the main part - the idea is that we'll create a helper class that will contain one attached dependency property that will assign an owner DropDownButton
control to the ContextMenu
responsible for presenting its items. Upon changing the owner we'll observe its DropDownButton.GroupStyle
collection and use ContextMenu.GroupStyleSelector
to feed the ContextMenu
with items coming from its owner's collection. Here's the code:
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
:
In order to complete the second step we need to bind the Owner
property for the ContextMenu
(we'll use DropDownButton.MenuStyle
to do that) and add some GroupStyle
items to the 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>
我认为这应该足以实现您的目标.
This I think should be enough to achieve your goal.
这篇关于在 DropDownButton 中组织项目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!