WPF 将枚举列表(或类似内容)绑定到复选框列表

本文关键字:列表 绑定 复选框 枚举 WPF | 更新日期: 2023-09-27 18:32:06

我想将复选框列表绑定到 WPF 中的枚举值集合。枚举不是 [标志]。

上下文:它用于过滤数据网格,其中每个项目都有一个我的枚举实例。它不一定需要绑定到 List,固定大小的集合也可以。

WPF 将枚举列表(或类似内容)绑定到复选框列表

假设你想绑定到枚举的所有可能值,你可以使用 ObjectDataProvider 来实现。在您的资源中声明这一点(Window.ResourcesApp.Resources等):

    <ObjectDataProvider x:Key="enumValues" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="local:TestEnum"/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>

这基本上表示对Enum.GetValues(typeof(TestEnum))的调用,并将其公开为数据源。注意:您需要声明之前syslocal命名空间,其中sysclr-namespace:System;assembly=mscorliblocal是枚举的命名空间。

拥有它后,您可以像其他任何内容一样将该 ObjectDataProvider 用作绑定源,例如:

<ListBox ItemsSource="{Binding Source={StaticResource enumValues}}"/>

执行此操作的非声明性方法只是在代码中分配它:

someListBox.ItemsSource = Enum.GetValues(typeof(TestEnum));

对于绑定所选项,很遗憾,无法从 Xaml 设置 SelectedItems 属性,但您可以使用 SelectionChanged 事件:

<ListBox Name="lb" ItemsSource="{Binding Source={StaticResource enumValues}}" SelectionMode="Multiple" SelectionChanged="lb_SelectionChanged"></ListBox>

然后在以下情况下在视图模型(或您使用的任何内容)上设置属性:

private void lb_SelectionChanged(object sender, SelectionChangedEventArgs e) {
    viewModel.SelectedValues = lb.SelectedItems.OfType<TestEnum>().ToList();
}

这个适合你吗?它将任何枚举转换为字典,以便您可以访问枚举的内部整数以及它们的名称(用于显示)。

using System;
using System.Collections.Generic;
using System.Linq;
namespace Sample
{
    class Sample
    {
        public static IDictionary<String, Int32> ConvertEnumToDictionary<K>()
        {
            if (typeof(K).BaseType != typeof(Enum))
            {
                throw new InvalidCastException();
            }
            return Enum.GetValues(typeof(K)).Cast<Int32>().ToDictionary(currentItem => Enum.GetName(typeof(K), currentItem));
        }
    }
}

编辑

可以使用 ICollection 类型的 IDictionary 属性KeysValues来执行所需的绑定。

myListBox.ItemsSource = myEnumDictionary.Keys;

当然,您也可以直接在 XAML 中执行此操作。

<ListBox ItemsSource="{Binding myEnumDictionary.Keys}"></ListBox>

下面介绍如何在没有任何代码隐藏或视图中定义的 DataObjectProviders 的情况下执行此操作。

步骤 1:创建一个可以存储ListBoxItem值的新类,以及是否选择它的属性。

它需要实现INotifyPropetyChanged,以便支持是否选择的双向绑定。我将它设为通用,因此只需要定义一次,并且可以重用于任何类型的类型。如果您已经有一个实现 INotifyPropertyChanged 的基类,则可以从该基类继承并忽略此类的前几行。

public class SelectableItem<T> : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
  public SelectableItem(T val)
  {
    Value = val;
    _isSelected = false;
  }
  public T Value { get; private set; }
  private bool _isSelected;
  public bool IsSelected
  {
    get => _isSelected;
    set
    {
      if (_isSelected == value) return;
      _isSelected = value;
      OnPropertyChanged();
    }
  }
}

第 2 步:将SelectableItem属性的集合添加到 ViewModel/DataContext。由于您的项目列表是可用枚举值的静态列表,因此无需将其实现为 ObservableCollection 或任何类似的东西。项目源只需要 1 次绑定。

public class ViewModel : ViewModelBase
{
  public ViewModel()
  {
    AvailableItems = typeof(TestEnum).GetEnumValues().Cast<TestEnum>().Select((e) => new SelectableItem<TestEnum>(e)).ToList();
  }
  public IEnumerable<SelectableItem<TestEnum>> AvailableItems { get; private set; }
}

步骤 3:使用 ItemContainerStyle 允许绑定到ListBoxItemIsSelected 属性:

<ListBox ItemsSource="{Binding AvailableItems, Mode=OneTime}" SelectionMode="Extended">
  <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
      <Setter Property="Content" Value="{Binding Value}"/>
      <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

若要获取所选项,只需循环访问"可用项"属性并查找具有 IsSelected = true 的项。您甚至可以添加只读属性来执行此操作:

public IEnumerable<TestEnum> SelectedItems
{
  get => AvailableItems.Where((o) => o.IsSelected).Select((o) => o.Value).ToList();
}

@Dummy01说的是正确的,但我认为大多数人都需要得到结果如

Dictionary<int,string>

Dictionary<string,int>

所以解决方案应该是

using System;
using System.Collections.Generic;
using System.Linq;
namespace Sample
{
    class Sample
    {
        public static IDictionary<String, Int32> ConvertEnumToDictionary<K>()
        {
            if (typeof(K).BaseType != typeof(Enum))
            {
                throw new InvalidCastException();
            }
            return Enum.GetValues(typeof(K)).Cast<Int32>().ToDictionary(i => i, i => Enum.GetName(typeof(K), i));
        }
    }
}

所以现在你会得到KeyValuePair<Int32,string>

如果您将枚举用作 FLAGS,这将非常有用

[Flags]
public enum LogSystemLogType
{
   All = 1 << 1,
   CreateDatabase = 1 << 2,
   CreateContract = 1 << 3,
}