【问题标题】:Organize Items in DropDownButton?在 DropDownButton 中组织项目?
【发布时间】:2017-02-17 23:39:08
【问题描述】:

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

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 属性的任何标题,DropDown 内的项目没有标题而是作为列表进行组织,因此没有组织。 我正在尝试获取this predisposition.

我做错了什么?

【问题讨论】:

    标签: c# wpf xaml mahapps.metro


    【解决方案1】:

    预赛

    在 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.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>
    

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

    【讨论】:

    • 我得到这个结果:imgur.com/a/hOi6e 可能是因为我正在使用 CheckedListBox 类? pastebin.com/gJA2zWzz
    • 我只发布了有关设置DropDownButton 控件的代码的相关部分 - 你仍然需要自己提供DisplayMemberPathItemTemplateItemTemplateSelector - 你有ItemTemplate="{StaticResource CombinedTemplate}" 在您的问题中,所以我将从使用它开始。如果您仍然得到此结果,则表示您的模板中存在一些错误,在这种情况下,您应该同时发布 CheckedListItem&lt;T&gt; 类代码以及模板 XAML。
    【解决方案2】:

    如果您查看已链接到的另一篇文章,答案就应有尽有 - 特别是您需要绑定到 CollectionView,而不是直接绑定到集合。然后你可以在 CollectionView 上设置分组。

    所以,在你的情况下,定义属性:

    public ICollectionView LeaguesView { get; private set; }
    

    然后,在您创建了 Leagues 收藏后,将视图附加到您的收藏中,然后在视图上设置分组:

    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 属性。

    【讨论】:

    • 我不知道,但是我在 DropDownButton 中没有显示任何项目,我已经创建了一个包含所有代码的粘贴,您可以看看吗? pastebin.com/BWVsjEFd
    • 我看不到任何明显的东西,但有几点: - 你只需要创建一次视图,而不是每次添加一个国家。 - 您还在最初的问题之上添加了一层复杂性,但使用 CheckedListItem,也许在没有它的情况下先让它工作 - 尝试将 IsSynchronizedWithCurrentItem="true" 添加到您的 DropDownButton xaml。 - LeaguesView 上的 OnPropertyChanged 可能也有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-15
    • 2012-10-21
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多