【发布时间】:2012-05-05 20:07:22
【问题描述】:
我想将复选框列表绑定到 WPF 中的枚举值集合。 枚举不是 [Flags]。
上下文: 它用于过滤数据网格,其中每个项目都有一个我的枚举实例。 它不一定需要绑定到 List,固定大小的集合也可以。
【问题讨论】:
标签: c# wpf data-binding binding mvvm
我想将复选框列表绑定到 WPF 中的枚举值集合。 枚举不是 [Flags]。
上下文: 它用于过滤数据网格,其中每个项目都有一个我的枚举实例。 它不一定需要绑定到 List,固定大小的集合也可以。
【问题讨论】:
标签: c# wpf data-binding binding mvvm
假设您想绑定到枚举的所有可能值,您可以使用ObjectDataProvider 来完成。
在您的资源中声明这一点(Window.Resources 或 App.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)) 的调用并将其公开为数据源。
注意:您需要先声明命名空间sys 和local,其中sys 是clr-namespace:System;assembly=mscorlib,local 是您的枚举的命名空间。
一旦你有了它,你就可以像其他任何东西一样将该 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>
然后在事件中设置您的 ViewModel(或您使用的任何东西)上的属性:
private void lb_SelectionChanged(object sender, SelectionChangedEventArgs e) {
viewModel.SelectedValues = lb.SelectedItems.OfType<TestEnum>().ToList();
}
【讨论】:
ObjectType="{x:Type sys:Enum}" 上的 DataObjectProvider 出错:“命名空间“clr-namespace:System;assembly=System”中不存在名称“Enum”。
sys 必须映射到"clr-namespace:System;assembly=mscorlib"(不是程序集System)
这个适合你吗?它将任何 Enum 转换为字典,以便您可以访问 Enum 的内部整数以及它们的名称(用于显示)。
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 属性 Keys 和 Values 来执行您想要的绑定。
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 或任何类似的东西。 ItemsSource 只需要 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 允许绑定到 ListBoxItems 的 IsSelected 属性:
<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>
要获取选定的项目,只需遍历 AvailableItems 属性并找到 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,
}
【讨论】: