当前位置:  开发笔记 > 编程语言 > 正文

将枚举属性数据绑定到WPF中的ComboBox

如何解决《将枚举属性数据绑定到WPF中的ComboBox》经验,为你挑选了8个好方法。

作为示例,请使用以下代码:

public enum ExampleEnum { FooBar, BarFoo }

public class ExampleClass : INotifyPropertyChanged
{
    private ExampleEnum example;

    public ExampleEnum ExampleProperty 
    { get { return example; } { /* set and notify */; } }
}

我想要将属性ExampleProperty数据绑定到ComboBox,以便它显示选项"FooBar"和"BarFoo"并在TwoWay模式下工作.最理想的是我希望我的ComboBox定义看起来像这样:


目前我在我的Window中安装了ComboBox.SelectionChanged和ExampleClass.PropertyChanged事件的处理程序,我手动执行绑定.

是否有更好或某种规范的方式?您通常会使用转换器吗?如何使用正确的值填充ComboBox?我现在甚至不想开始使用i18n.

编辑

所以回答了一个问题:如何使用正确的值填充ComboBox.

通过静态Enum.GetValues方法中的ObjectDataProvider将Enum值作为字符串列表检索:


    
        
            
        
    

这个我可以用作我的ComboBox的ItemsSource:


Gregor Slave.. 205

您可以创建自定义标记扩展.

用法示例:

enum Status
{
    [Description("Available.")]
    Available,
    [Description("Not here right now.")]
    Away,
    [Description("I don't have time right now.")]
    Busy
}

在XAML的顶部:

    xmlns:my="clr-namespace:namespace_to_enumeration_extension_class

然后...

 

并实施......

public class EnumerationExtension : MarkupExtension
  {
    private Type _enumType;


    public EnumerationExtension(Type enumType)
    {
      if (enumType == null)
        throw new ArgumentNullException("enumType");

      EnumType = enumType;
    }

    public Type EnumType
    {
      get { return _enumType; }
      private set
      {
        if (_enumType == value)
          return;

        var enumType = Nullable.GetUnderlyingType(value) ?? value;

        if (enumType.IsEnum == false)
          throw new ArgumentException("Type must be an Enum.");

        _enumType = value;
      }
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      var enumValues = Enum.GetValues(EnumType);

      return (
        from object enumValue in enumValues
        select new EnumerationMember{
          Value = enumValue,
          Description = GetDescription(enumValue)
        }).ToArray();
    }

    private string GetDescription(object enumValue)
    {
      var descriptionAttribute = EnumType
        .GetField(enumValue.ToString())
        .GetCustomAttributes(typeof (DescriptionAttribute), false)
        .FirstOrDefault() as DescriptionAttribute;


      return descriptionAttribute != null
        ? descriptionAttribute.Description
        : enumValue.ToString();
    }

    public class EnumerationMember
    {
      public string Description { get; set; }
      public object Value { get; set; }
    }
  }

+1,但是WPF完成最简单的事情所需的代码量实际上是令人头疼的 (30认同)

@Crown'my'是你在xaml文件顶部声明的命名空间前缀:例如xmlns:my ="clr-namespace:namespace_to_enumeration_extension_class.枚举是EnumerationExtension的缩写,在xaml中你不必编写整个扩展类名. (13认同)

@Gregor S.我的:枚举是什么? (7认同)

另一个限制是,如果您有多种语言,则无法执行此操作. (5认同)


小智.. 171

在viewmodel中,您可以:

public MyEnumType SelectedMyEnumType 
{
    get { return _selectedMyEnumType; }
    set { 
            _selectedMyEnumType = value;
            OnPropertyChanged("SelectedMyEnumType");
        }
}

public IEnumerable MyEnumTypeValues
{
    get
    {
        return Enum.GetValues(typeof(MyEnumType))
            .Cast();
    }
}

在XAML中,ItemSource绑定到MyEnumTypeValues,SelectedItem绑定到SelectedMyEnumType.



CoperNick.. 85

我不想在UI中使用枚举的名称.我更喜欢为user(DisplayMemberPath)使用不同的值,并为value(在这种情况下为enum)使用不同的值(SelectedValuePath).这两个值可以打包KeyValuePair并存储在字典中.

XAML

 

C#

public Dictionary ExampleEnumsWithCaptions { get; } =
    new Dictionary()
    {
        {ExampleEnum.FooBar, "Foo Bar"},
        {ExampleEnum.BarFoo, "Reversed Foo Bar"},
        //{ExampleEnum.None, "Hidden in UI"},
    };


private ExampleEnum example;
public ExampleEnum ExampleProperty
{
    get { return example; }
    set { /* set and notify */; }
}

编辑:与MVVM模式兼容.



1> Gregor Slave..:

您可以创建自定义标记扩展.

用法示例:

enum Status
{
    [Description("Available.")]
    Available,
    [Description("Not here right now.")]
    Away,
    [Description("I don't have time right now.")]
    Busy
}

在XAML的顶部:

    xmlns:my="clr-namespace:namespace_to_enumeration_extension_class

然后...

 

并实施......

public class EnumerationExtension : MarkupExtension
  {
    private Type _enumType;


    public EnumerationExtension(Type enumType)
    {
      if (enumType == null)
        throw new ArgumentNullException("enumType");

      EnumType = enumType;
    }

    public Type EnumType
    {
      get { return _enumType; }
      private set
      {
        if (_enumType == value)
          return;

        var enumType = Nullable.GetUnderlyingType(value) ?? value;

        if (enumType.IsEnum == false)
          throw new ArgumentException("Type must be an Enum.");

        _enumType = value;
      }
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      var enumValues = Enum.GetValues(EnumType);

      return (
        from object enumValue in enumValues
        select new EnumerationMember{
          Value = enumValue,
          Description = GetDescription(enumValue)
        }).ToArray();
    }

    private string GetDescription(object enumValue)
    {
      var descriptionAttribute = EnumType
        .GetField(enumValue.ToString())
        .GetCustomAttributes(typeof (DescriptionAttribute), false)
        .FirstOrDefault() as DescriptionAttribute;


      return descriptionAttribute != null
        ? descriptionAttribute.Description
        : enumValue.ToString();
    }

    public class EnumerationMember
    {
      public string Description { get; set; }
      public object Value { get; set; }
    }
  }


+1,但是WPF完成最简单的事情所需的代码量实际上是令人头疼的
@Crown'my'是你在xaml文件顶部声明的命名空间前缀:例如xmlns:my ="clr-namespace:namespace_to_enumeration_extension_class.枚举是EnumerationExtension的缩写,在xaml中你不必编写整个扩展类名.
@Gregor S.我的:枚举是什么?
另一个限制是,如果您有多种语言,则无法执行此操作.

2> 小智..:

在viewmodel中,您可以:

public MyEnumType SelectedMyEnumType 
{
    get { return _selectedMyEnumType; }
    set { 
            _selectedMyEnumType = value;
            OnPropertyChanged("SelectedMyEnumType");
        }
}

public IEnumerable MyEnumTypeValues
{
    get
    {
        return Enum.GetValues(typeof(MyEnumType))
            .Cast();
    }
}

在XAML中,ItemSource绑定到MyEnumTypeValues,SelectedItem绑定到SelectedMyEnumType.




3> CoperNick..:

我不想在UI中使用枚举的名称.我更喜欢为user(DisplayMemberPath)使用不同的值,并为value(在这种情况下为enum)使用不同的值(SelectedValuePath).这两个值可以打包KeyValuePair并存储在字典中.

XAML

 

C#

public Dictionary ExampleEnumsWithCaptions { get; } =
    new Dictionary()
    {
        {ExampleEnum.FooBar, "Foo Bar"},
        {ExampleEnum.BarFoo, "Reversed Foo Bar"},
        //{ExampleEnum.None, "Hidden in UI"},
    };


private ExampleEnum example;
public ExampleEnum ExampleProperty
{
    get { return example; }
    set { /* set and notify */; }
}

编辑:与MVVM模式兼容.


我认为你的答案被低估了,考虑到ComboBox本身的预期,它似乎是最好的选择.也许您可以使用"Enum.GetValues"将字典构建器放在getter中,但这不会解决要显示的名称部分.最后,特别是如果实现了I18n,无论如何,如果枚举发生变化,你将不得不手动更改内容.但是枚举不应该经常改变,如果有的话,是吗?+1
Dictionary的问题在于键是按哈希值排序的,所以几乎没有控制权.虽然稍微冗长一点,但我使用了List >.好主意.
@CoperNick @Pragmateek新修复:`public Dictionary ExampleEnumsWithCaptions {get; } = new Dictionary (){{ExampleEnum.FooBar,"Foo Bar"},{ExampleEnum.BarFoo,"Reversed Foo Bar"},// {ExampleEnum.None,"隐藏在UI"},} ;`

4> rudigrobler..:

我不知道是否可以仅在XAML中使用,但请尝试以下方法:

为ComboBox命名,以便在代码隐藏中访问它:"typesComboBox1"

现在尝试以下方法

typesComboBox1.ItemsSource = Enum.GetValues(typeof(ExampleEnum));



5> Martin Liver..:

根据ageektrapped提供的已接受但现已删除的答案,我创建了一个没有一些更高级功能的精简版本.这里包含所有代码,允许您复制粘贴它,而不是被link-rot阻止.

我使用的System.ComponentModel.DescriptionAttribute是真正用于设计时的描述.如果您不喜欢使用此属性,您可以创建自己的属性,但我认为使用此属性确实可以完成工作.如果不使用该属性,则名称将默认为代码中枚举值的名称.

public enum ExampleEnum {

  [Description("Foo Bar")]
  FooBar,

  [Description("Bar Foo")]
  BarFoo

}

以下是用作项目来源的类:

public class EnumItemsSource : Collection, IValueConverter {

  Type type;

  IDictionary valueToNameMap;

  IDictionary nameToValueMap;

  public Type Type {
    get { return this.type; }
    set {
      if (!value.IsEnum)
        throw new ArgumentException("Type is not an enum.", "value");
      this.type = value;
      Initialize();
    }
  }

  public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) {
    return this.valueToNameMap[value];
  }

  public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) {
    return this.nameToValueMap[value];
  }

  void Initialize() {
    this.valueToNameMap = this.type
      .GetFields(BindingFlags.Static | BindingFlags.Public)
      .ToDictionary(fi => fi.GetValue(null), GetDescription);
    this.nameToValueMap = this.valueToNameMap
      .ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    Clear();
    foreach (String name in this.nameToValueMap.Keys)
      Add(name);
  }

  static Object GetDescription(FieldInfo fieldInfo) {
    var descriptionAttribute =
      (DescriptionAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute));
    return descriptionAttribute != null ? descriptionAttribute.Description : fieldInfo.Name;
  }

}

您可以在XAML中使用它,如下所示:


  

 



6> druss..:

使用ObjectDataProvider:


      
           
      
 

然后绑定到静态资源:

ItemsSource="{Binding Source={StaticResource enumValues}}"

在此博客中找到此解决方案



7> Greg..:

你可以考虑这样的事情:

    为textblock或要用于显示枚举的任何其他控件定义样式:

    
    

    为ComboBoxItem定义您的样式

    
    

    添加一个组合框并使用您的枚举值加载它:

    
        
            
                Value1
            
        
    
    

如果你的枚举很大,你当然可以在代码中做同样的事情,节省大量的打字.我喜欢这种方法,因为它使本地化变得容易 - 你定义了所有模板一次,然后,你只更新你的字符串资源文件.



8> Nick..:

我最喜欢的方法是使用,ValueConverter这样ItemsSource和SelectedValue都绑定到同一属性。这不需要其他属性即可保持ViewModel的美观和整洁。


以及转换器的定义:

public static class EnumHelper
{
  public static string Description(this Enum e)
  {
    return (e.GetType()
             .GetField(e.ToString())
             .GetCustomAttributes(typeof(DescriptionAttribute), false)
             .FirstOrDefault() as DescriptionAttribute)?.Description ?? e.ToString();
  }
}

[ValueConversion(typeof(Enum), typeof(IEnumerable))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return Enum.GetValues(value.GetType())
               .Cast()
               .Select(e => new ValueDescription() { Value = e, Description = e.Description()})
               .ToList();
  }
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return null;
  }
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    return this;
  }
}

该转换器可与任何枚举一起使用。ValueDescription只是具有Value属性和Description属性的简单类。您可以轻松地使用Tuplewith Item1和and Item2,或者使用KeyValuePairwith KeyValue代替Value and Description或您选择的任何其他类,只要它可以容纳枚举值和该枚举值的字符串描述即可。

推荐阅读
无名有名我无名_593
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有