WPF DataGrid 带有一键组合框,显示按枚举名称排序的枚举值
Posted
技术标签:
【中文标题】WPF DataGrid 带有一键组合框,显示按枚举名称排序的枚举值【英文标题】:WPF DataGrid with a one click ComboBox showing enumeration values sorted by enumeration names 【发布时间】:2021-11-30 02:40:46 【问题描述】:要求
我想向 WPF DataGrid 添加一个带有组合框的列,以满足以下要求:
-
ComboBox中显示的值应该是枚举常量的名称
ComboBox 中的条目应按枚举常量的名称排序
底层对象中的属性类型应该是枚举,而不是字符串
应减少点击次数。当我使用 DataGridComboBoxColumn 时,我需要大约 4 次单击来更改一个值。
我实际上喜欢代码隐藏解决方案,尽管基于 XAML 的解决方案也很好。
它应该在 .NET 5 WPF 下运行
示例应用程序
应用程序使用 DataGridComboBoxColumn 中提供的代码。它有效,但有两个问题:
详细信息下拉列表按字母顺序列出条目。在我的实际应用程序中,我有更多条目,当它们没有排序时很难找到正确的条目。
需要点击 4 次鼠标来更改 ComboBox 的值。
代码
XAML:
<Window x:Class="SortedComboBoxDataGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SortedComboBoxDataGrid"
xmlns:core="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="WidthAndHeight">
<Window.Resources>
<CollectionViewSource x:Key="SamplesViewSource" CollectionViewType="ListCollectionView"/>
<ObjectDataProvider x:Key="myEnum" MethodName="GetValues" ObjectType="x:Type core:Enum">
<ObjectDataProvider.MethodParameters>
<x:Type Type="local:DetailEnum"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<DataGrid x:Name="MainDataGrid" DataContext="StaticResource SamplesViewSource" ItemsSource="Binding"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="Binding Path=SomeText" Header="SomeText"/>
<DataGridComboBoxColumn Header="Detail" SelectedItemBinding="Binding Detail"
ItemsSource="Binding Source=StaticResource myEnum"/>
</DataGrid.Columns>
</DataGrid>
</Window>
枚举DetailEnum:
namespace SortedComboBoxDataGrid
public enum DetailEnum
No,
Some,
Many,
All
示例类:
public class Sample
public string SomeText get; set;
public DetailEnum Detail get; set;
public Sample(string someText, DetailEnum detail)
SomeText = someText;
Detail = detail;
后面的窗口代码:
using System.Collections.Generic;
using System.Windows;
namespace SortedComboBoxDataGrid
public partial class MainWindow: Window
public MainWindow()
InitializeComponent();
var samples = new List<Sample>()
new Sample("first", DetailEnum.All),
new Sample("second", DetailEnum.Many),
new Sample("any", DetailEnum.Some),
new Sample("last", DetailEnum.No),
;
var samplesViewSource = ((System.Windows.Data.CollectionViewSource)(FindResource("SamplesViewSource")));
samplesViewSource.Source = samples;
我已经尝试过了
我试过Displaying sorted enum values in a ComboBox。这很好地对枚举进行了排序,但这样做会将枚举值转换为字符串,然后对这些字符串进行排序。如果用户点击不同的条目,网格会返回一个字符串而不是枚举值。
我尝试了在 *** 上找到的各种解决方案来减少技巧的数量,但无法让其中一种与排序 (!) 枚举一起正常工作。
我想知道对于 ComboBox 使用具有枚举值和枚举名称作为其属性而不是 List 的类实例列表是否会更好?
public class DetailEnumClass
public DetailEnum EnumValue get; set;
public string EnumName get; set;
请在将此问题标记为重复问题之前阅读此内容
我知道关于我在这里提到的一个或另一个问题,*** 上已经有很多答案。但是,我无法提出涵盖所有要求的可行解决方案。因此,如果您找到了为完整问题提供完整代码的答案,请仅将此问题标记为重复。谢谢。
【问题讨论】:
【参考方案1】:您可以将DataGridTemplateColumn
与ComboBox
结合使用,该ComboBox
绑定到您在代码隐藏中创建的已排序IEnumerable<DetailEnum>
:
public partial class MainWindow : Window
public MainWindow()
Resources.Add("enums",
Enum.GetValues(typeof(DetailEnum)).Cast<DetailEnum>().OrderBy(x => x.ToString()));
InitializeComponent();
...
XAML:
<DataGrid x:Name="MainDataGrid" AutoGenerateColumns="False">
<DataGrid.Resources>
<DataTemplate x:Key="dt">
<ComboBox ItemsSource="StaticResource enums"
SelectedItem="Binding Detail, UpdateSourceTrigger=PropertyChanged" />
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="Binding Path=SomeText" Header="SomeText"/>
<DataGridTemplateColumn Header="Detail" CellTemplate="StaticResource dt" CellEditingTemplate="StaticResource dt" />
</DataGrid.Columns>
</DataGrid>
【讨论】:
就这么简单?谢谢你的建议。不幸的是,我的电脑正在维修中,因此需要 2 周以上的时间才能试用。 我想知道将 IEnumerable 转换为 Array 是否会更好地提高性能?如果网格中有许多组合框,每个组合框都必须执行 GetValues(),即创建数组并对该数组进行多次排序,涉及调用 ToString() 并因此创建大量字符串?一个 ComboBox 可能需要多次这样做:首先是当它显示已选择的值时,然后是当用户更改该值时。如果源是可枚举的,ComboBox 没有其他选择,只能在搜索某些内容时运行包括排序在内的整个查询?【参考方案2】:当我使用我接受的答案时,我遇到了一个有趣的问题。以下代码工作正常:
<Window.Resources>
<CollectionViewSource x:Key="SamplesViewSource"
CollectionViewType="ListCollectionView"/>
</Window.Resources>
但我不得不合并一些其他资源。第一步是把上面的代码改成这样:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
</ResourceDictionary.MergedDictionaries>
<CollectionViewSource x:Key="SamplesViewSource"
CollectionViewType="ListCollectionView"/>
</ResourceDictionary>
</Window.Resources>
之后我无法再运行该应用程序。我收到错误消息“找不到名为“枚举”的资源。资源名称区分大小写。”
我花了 2 天时间才弄清楚这里发生了什么。通常,不需要将<ResourceDictionary>
添加到<Window.Resources>
,XAML 会在幕后为您完成。为了能够使用MergedDictionaries
,您必须自己动手。这会改变 XAML 的行为方式。也许我应该在这里补充一下,因为这类问题,我讨厌 XAML 已有 10 多年了。
我猜想<ResourceDictionary>
XAML 会用新的ResourceDictionary
删除自动创建的ResourceDictionary
,旧的内容会丢失,即enums
,它在调用InitializeComponent()
之前已添加到Windows.Resources
。
我认为没什么大不了的,并尝试在InitializeComponent()
之后添加enums
,因为只有在我使用以下行将数据填充到DataGrid.DataContext
时才需要enums
:
samplesViewSource.Source = samples;
但我仍然收到相同的错误消息。我的猜测是 DataGrid.Resources
中定义的 ComboBox 的 DataTemplate
在创建 DataGrid
时被执行。此时enums
尚未添加。
我可以通过使用 dynamic 而不是 static 资源来消除错误:
<ComboBox ItemsSource="DynamicResource enums"
此时,我对 XAML 给出的所有问题感到非常恼火,并决定不使用 Resources
代替 ComboBox.ItemsSource
,而是使用 DataContext
。
当然,这又带来了另一个问题。我在文档中找不到它,但使用调试器我发现ComboBox.DataContext
是Sample
而不是从DataGrid
继承的DataContext
,这是有道理的。每行需要访问DataGrid.ItemsSource
中的不同项目。
又过了一天,我想出了如何从Combobox DataTemplate
访问DataGrid.DataContext
:
<DataTemplate x:Key="dt">
<ComboBox ItemsSource="Binding RelativeSource=RelativeSource FindAncestor,
AncestorType=x:Type DataGrid,
Path=DataContext.DetailEnums"
SelectedItem="Binding Detail, UpdateSourceTrigger=PropertyChanged"
</DataTemplate>
如果您像我一样尝试尽量减少 XAML 造成的问题,这里是您如何在 DataGrid
中使用 ComboBoxes
而不使用 Resources
来保存数据的完整代码:
<Window x:Class="SortedComboBoxDataGrid.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="WidthAndHeight">
<DataGrid x:Name="MainDataGrid" ItemsSource="Binding SamplesViewSource" AutoGenerateColumns="False">
<DataGrid.Resources>
<DataTemplate x:Key="dt">
<ComboBox ItemsSource="Binding RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type DataGrid,
Path=DataContext.DetailEnums"
SelectedItem="Binding Detail, UpdateSourceTrigger=PropertyChanged"/>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="Binding Path=SomeText" Header="SomeText"/>
<DataGridTemplateColumn Header="Detail" CellTemplate="StaticResource dt" CellEditingTemplate="StaticResource dt" />
</DataGrid.Columns>
</DataGrid>
</Window>
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace SortedComboBoxDataGrid
public partial class Window2: Window
public record MainDataGridDataContext(ListCollectionView SamplesViewSource, DetailEnum[] DetailEnums);
public Window2()
InitializeComponent();
var samples = new List<Sample>()
new Sample("first", DetailEnum.All),
new Sample("second", DetailEnum.Many),
new Sample("any", DetailEnum.Some),
new Sample("last", DetailEnum.No),
;
var samplesViewSource = new ListCollectionView(samples);
var detailEnums = Enum.GetValues(typeof(DetailEnum)).Cast<DetailEnum>().OrderBy(x => x.ToString()).ToArray(); ;
MainDataGrid.DataContext = new MainDataGridDataContext(samplesViewSource, detailEnums);
【讨论】:
以上是关于WPF DataGrid 带有一键组合框,显示按枚举名称排序的枚举值的主要内容,如果未能解决你的问题,请参考以下文章
在 MVVM 中的 Datagrid 中绑定 WPF 组合框不保存更改
WPF DataGrid - 从 DataGrid ItemsSource 对象的集合值中设置唯一的每行(对象)组合框值
WPF DataGridTemplateColumn组合框问题