WPF - ComboBox 和 ListBox 的 ItemsSource 自动绑定 enum 值集合
前言
WPF 的 ComboBox 控件等绑定 enum 值很繁琐,很让人头疼,网上也有提供了一些方法,基本是使用 ObjectDataProvider 方式和 MarkupExtension 方式,
有没有办法绑定值为 enum 类型就自动加载所有枚举值选项,下面记录一种方法;
实现方式
主要通过附加属性,根据绑定的 Selecter.SelectedItem 属性,获取属性类型,再获取枚举值的集合了,下面是实现代码:
添加附加属性 ItemsControlHelper.EnumValuesToItemsSourceProperty(修改嵌套属性获取类型)
public class ItemsControlHelper { /// <summary> /// 绑定 enum 类型所有值给 ItemsSource 赋值 /// 必须绑定 SelectedItem /// </summary> public static readonly DependencyProperty EnumValuesToItemsSourceProperty = DependencyProperty.RegisterAttached( "EnumValuesToItemsSource", typeof(bool), typeof(ItemsControlHelper), new PropertyMetadata(default(bool), OnEnumValuesToItemsSourceChanged)); public static void SetEnumValuesToItemsSource(DependencyObject element, bool value) { element.SetValue(EnumValuesToItemsSourceProperty, value); } public static bool GetEnumValuesToItemsSource(DependencyObject element) { return (bool)element.GetValue(EnumValuesToItemsSourceProperty); } private static void OnEnumValuesToItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is ItemsControl itemsControl && GetEnumValuesToItemsSource(itemsControl)) { if (itemsControl.IsLoaded) { SetItemsSource(itemsControl); } else { itemsControl.Loaded += ItemsControl_Loaded; } } } private static void SetItemsSource(ItemsControl itemsControl) { var itemsBindingExpression = BindingOperations.GetBinding(itemsControl, ItemsControl.ItemsSourceProperty); if (itemsBindingExpression != null) { throw new InvalidOperationException("When using ItemsControlHelper.EnumValuesToItemsSource, cannot be used ItemsSource at the same time."); } if (itemsControl.Items.Count > 0) { throw new InvalidOperationException("When using ItemsControlHelper.EnumValuesToItemsSource, Items Collection must be null"); } var bindingExpression = BindingOperations.GetBindingExpression(itemsControl, Selector.SelectedItemProperty); if (bindingExpression == null) { throw new InvalidOperationException("ItemsControl must be binding SelectedItem property"); } var binding = bindingExpression.ParentBinding; var dataType = bindingExpression.DataItem?.GetType(); var paths = binding.Path.Path.Split('.'); foreach (var path in paths) { var propertyInfo = dataType?.GetProperty(path); if (propertyInfo == null) { return; } dataType = propertyInfo.PropertyType; } if (!dataType!.IsEnum) { var underlyingType = Nullable.GetUnderlyingType(dataType); if (underlyingType == null) { return; } dataType = underlyingType; } var itemsSourceBinding = new Binding(); itemsSourceBinding.Source = Enum.GetValues(dataType); itemsSourceBinding.Mode = BindingMode.OneWay; itemsControl.SetBinding(ItemsControl.ItemsSourceProperty, itemsSourceBinding); } private static void ItemsControl_Loaded(object sender, RoutedEventArgs e) { var itemsControl = (ItemsControl)sender; itemsControl.Loaded -= ItemsControl_Loaded; SetItemsSource(itemsControl); } }
viewmodel 代码创建 枚举类型属性
public class MainViewModel : ObservableObject { private Animal animal; public Animal Animal { get { return animal; } set { SetProperty(ref animal, value); } } } public enum Animal { Dog = 0, Cat, Elephant, Bird, Lion, Tiger }
前端 xaml 代码,将 ComboBox.SelectedItem 绑定枚举属性,并设置 ItemsControlHelper.EnumValuesToItemsSource="True"
<Grid> <ComboBox Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" local:ItemsControlHelper.EnumValuesToItemsSource="True" SelectedItem="{Binding Animal}" /> </Grid>
运行代码,自动加载枚举值集合到 ComboBox.ItemsSource
还可以添加 TypeConverter 的方式显示为自定义字符串,新增一个 EnumDescriptionConverter .cs
public class EnumDescriptionConverter : EnumConverter { public EnumDescriptionConverter(Type type) : base(type) { } /// <inheritdoc/> public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type? destinationType) { if (destinationType == typeof(string)) { if (value != null) { FieldInfo? fieldInfo = value.GetType()!.GetField(value.ToString()!); if (fieldInfo != null) { var attribute = fieldInfo.GetCustomAttribute<DescriptionAttribute>(inherit: false); if (attribute != null) { return !string.IsNullOrEmpty(attribute.Description) ? attribute.Description : value.ToString()!; } } return value.ToString()!; } } return string.Empty; } }
枚举添加 Attribute 注释内容
[TypeConverter(typeof(EnumDescriptionConverter))] public enum Animal { [Description("小狗")] Dog = 0, [Description("小猫")] Cat, [Description("大象")] Elephant, [Description("小鸟")] Bird, [Description("狮子")] Lion, [Description("小脑斧")] Tiger }
再次运行代码结果
同理继承 Selector 的控件也可以使用,例如 ListBox :
总结
最后只需要添加一行代码 local:ItemsControlHelper.EnumValuesToItemsSource="True",就可以自动绑定 ItemsSource,xaml 代码不需要再去关注 enum 的类型。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义