WPF DataGrid,其中包含按一下即可显示枚举值的ComboBox,并依枚举名称排序

idfiyjo8  于 2022-11-26  发布在  其他
关注(0)|答案(2)|浏览(177)

要求

我希望将具有组合框的列添加到满足以下要求的WPF DataGrid:

  1. ComboBox中显示的值应该是枚举常量的名称
  2. ComboBox中的条目应按枚举常量的名称排序
    1.基础对象中的属性类型应为枚举,而不是字符串
    1.点击次数应该减少,当我使用DataGridComboBoxColumn时,我需要大约4次点击才能更改一个值。
    1.我实际上喜欢代码隐藏解决方案,尽管基于XAML的解决方案也很好。
    1.它应该在.NET 5 WPF下运行

示例应用程序

该应用程序使用DataGridComboBoxColumn中提供的代码。它可以正常工作,但存在两个问题:
1.详细信息下拉列表按字母顺序列出条目。2在我的真实的应用程序中,我有更多的条目,当它们没有排序时,很难找到正确的条目。
1.更改ComboBox值需要单击4次鼠标。

代码

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>

枚举详细信息枚举:

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;
    }
  }
}

windows 程式码后置:

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,它对枚举进行了很好的排序,但是它将枚举值转换为字符串,然后对这些字符串进行排序。如果用户点击了不同的条目,网格将返回一个字符串而不是枚举值。
我尝试了在stackoverflow上找到的各种解决方案来减少技巧的数量,但无法使一个解决方案与sorted(!)枚举一起正常工作。
我想知道,对于ComboBox,使用一个类示例列表(具有枚举值和枚举名称作为其属性)是否比使用List更好?

public class DetailEnumClass {
    public DetailEnum EnumValue { get; set; }
    public string EnumName { get; set; }
  }

将此问题标记为重复问题之前,请阅读此内容

我知道已经有很多关于stackoverflow问题的答案,但是,我不能给出一个能满足所有需求的解决方案。所以,如果你找到了一个能提供完整问题的完整代码的答案,请把这个问题标记为重复。谢谢。

sc4hvdpw

sc4hvdpw1#

您可以将DataGridTemplateColumn与绑定到已排序IEnumerable<DetailEnum>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>
nc1teljy

nc1teljy2#

当我使用我接受的答案时,我遇到了一个有趣的问题。

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

之后,我无法再运行应用程序。我收到错误消息“* 找不到名为”enums“的资源。资源名称区分大小写。*”
我花了两天的时间才弄明白这里发生了什么。通常,人们不需要把<ResourceDictionary>添加到<Window.Resources>,XAML会在幕后为你做这件事。为了能够使用MergedDictionaries,你必须自己做。这改变了XAML的行为。也许我应该在这里补充一下,我讨厌XAML已经有10多年了,因为这类问题。
我猜使用<ResourceDictionary>时,XAML会用一个新的XAML删除自动创建的ResourceDictionary,旧的XAML的内容也会丢失,即enums,它是在调用InitializeComponent()之前添加到Windows.Resources的。
我认为没什么大不了的,并尝试在InitializeComponent()之后添加enums,因为只有在我使用以下行将数据填充到DataGrid.DataContext中时才需要enums

samplesViewSource.Source = samples;

但是我还是得到了同样的错误信息。我猜测是当DataGrid被创建时,在DataGrid.Resources中定义的ComboBox的DataTemplate被执行。此时,enums还没有被添加。
我可以通过使用动态而不是静态资源来消 debugging 误:

<ComboBox ItemsSource="{DynamicResource enums}"

在这一点上,我真的很恼火的所有问题XAML,并决定不使用ResourcesComboBox.ItemsSource,但DataContext
当然,这也带来了另一个问题。我在文档中找不到它,但通过调试器我发现ComboBox.DataContextSample,而不是从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);
    }
  }
}

相关问题