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 的类型。

 

posted @ 2022-09-30 11:16  好想写代码啊  阅读(281)  评论(2编辑  收藏  举报