jjzjj

WPF - ComboBox 和 ListBox 的 ItemsSource 自动绑定 enum 值集合

看到阳光 2023-03-28 原文

前言

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

 

有关WPF - ComboBox 和 ListBox 的 ItemsSource 自动绑定 enum 值集合的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  4. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

  5. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  6. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

  7. ruby - [1,2,3].to_enum 和 [1,2,3].enum_for 在 Ruby 中的区别 - 2

    在Ruby中,我试图理解to_enum和enum_for方法。在我提出问题之前,我提供了一些示例代码和两个示例来帮助理解上下文。示例代码:#replicatesgroup_bymethodonArrayclassclassArraydefgroup_by2(&input_block)returnself.enum_for(:group_by2)unlessblock_given?hash=Hash.new{|h,k|h[k]=[]}self.each{|e|hash[input_block.call(e)]示例#1:irb(main)>puts[1,2,3].group_by2.ins

  8. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

  9. ruby - 在 ruby​​ 中使用自动创建插入数组 - 2

    我想知道是否可以通过自动创建数组来插入数组,如果数组不存在的话,就像在PHP中一样:$toto[]='titi';如果尚未定义$toto,它将创建数组并将“titi”压入。如果已经存在,它只会推送。在Ruby中我必须这样做:toto||=[]toto.push('titi')可以一行完成吗?因为如果我有一个循环,它会测试“||=”,除了第一次:Person.all.eachdo|person|toto||=[]#with1billionofperson,thislineisuseless999999999times...toto.push(person.name)你有更好的解决方案吗?

  10. ruby - ruby 中绑定(bind)对象的实际使用 - 2

    昨晚,我在思考我认为是高级ruby​​语言的功能,即Continuations(callcc)和Bindingobjects。我的意思是高级,因为我有静态类型的oo语言背景(C#、Java、C++),我最近才发现ruby​​,所以这些语言特性对我来说不是很熟悉。我想知道这些语言功能在现实世界中的用途是什么。根据我的经验,一切都可以用静态类型的oo语言来完成,但有时我不太同意。我想我在阅读SamRuby的那篇好文章时发现了Continuation的美妙之处/兴趣:http://www.intertwingly.net/blog/2005/04/13/Continuations-for-C

随机推荐