2013-05-07 62 views
2

我已经阅读了WinForms ComboBox的ValueMember属性的主题很多有用的帖子,但他们都没有回答我的具体问题。首先,我将描述一个简单的例子,它可以正常工作,然后我将描述我想如何改变它,并询问如何做到这一点(我也会提供我的一个尝试)。通过ValueMember的ValueMember选择类的属性

一,工作示例。只是一个带有comboBox控件的表单,并且此代码如下:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     comboBox1.DataSource = Enum.GetValues(typeof(Enum1)); 
     comboBox1.SelectedItem = Enum1.MultiWordValue2; 
    } 
} 

public enum Enum1 : int 
{ 
    Undefined, 
    MultiWordValue1, 
    MultiWordValue2 
} 

没问题,这工作得很好。我运行应用程序,comboBox获取适当的项目,并选择适当的值。

但是,这些枚举值是丑陋的,我希望我的用户有更好的体验,所以我创建了一个扩展方法,使值更好地显示。

public static class ExtensionMethods 
{ 
    public static string ToDisplayString(this Enum1 me) 
    { 
     switch (me) 
     { 
      case Enum1.MultiWordValue1: 
       return "Multi Word Value 1"; 
      case Enum1.MultiWordValue2: 
       return "Multi Word Value 2"; 
      default: 
       return string.Empty; 
     } 
    } 
} 

我的问题是,如何最好我利用这个扩展方法,同时保持从枚举值的列表构建组合框项目,并能够通过设置选定ComboBox项(或价值)的能力枚举值?

以下是我的第一次尝试。我创建了一个简单的类来包装枚举值和显示字符串,并将组合框DisplayMember和ValueMember设置为新类的属性。这部分工作; ComboBox的项目与显示值正确填充,但我不能设置的SelectedValue:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     comboBox1.DisplayMember = "Display"; 
     comboBox1.ValueMember = "Value"; 
     foreach (Enum1 e in Enum.GetValues(typeof(Enum1))) 
      comboBox1.Items.Add(new ValueDisplayEnum(e, e.ToDisplayString())); 
     comboBox1.SelectedValue = Enum1.MultiWordValue2; 
    } 
} 

public enum Enum1 : int 
{ 
    Undefined, 
    MultiWordValue1, 
    MultiWordValue2 
} 

public static class ExtensionMethods 
{ 
    public static string ToDisplayString(this Enum1 me) 
    { 
     switch (me) 
     { 
      case Enum1.MultiWordValue1: 
       return "Multi Word Value 1"; 
      case Enum1.MultiWordValue2: 
       return "Multi Word Value 2"; 
      default: 
       return string.Empty; 
     } 
    } 
} 

public class ValueDisplayEnum 
{ 
    public object Value { get; set; } 
    public string Display { get; set; } 
    private ValueDisplayEnum() { } 
    public ValueDisplayEnum(object _Value, string _Display) 
    { 
     Value = _Value; 
     Display = _Display; 
    } 
} 

为什么不设置的SelectedValue这样的工作?我该如何完成一个更好的显示字符串的枚举组合框?

另:我认为这可能是因为ValueDisplayEnum的Value属性的底层数据类型是一个对象,而不是Enum1。但是,如果将类型更改为Enum1,则SelectedValue仍然无法正确设置。

感谢您的帮助!

编辑1:指定由Mike基于所述建议的一个解决方案:

添加以下模板化方法:

public static void SetEnumCombo<T>(ComboBox _ComboBox, T _Value) 
{ 
    foreach (ValueDisplayEnum vde in _ComboBox.Items) 
     if (((T)vde.Value).Equals(_Value)) 
      _ComboBox.SelectedItem = vde; 
} 

而不是指定直接的SelectedValue或selectedItem的,称此:

foreach (Enum1 e in Enum.GetValues(typeof(Enum1))) 
    comboBox1.Items.Add(new ValueDisplayEnum(e, e.ToDisplayString())); 
SetEnumCombo(comboBox1, Enum1.MultiWordValue2); 

一切都像魅力一样。我仍然不确定为什么按价值设置不起作用,但这是解决问题的非常紧凑的解决方案。但并不像回答那样紧凑!

+0

好设置的SelectedValue不工作,因为你有字符串数据,并希望与枚举值来选择它的组合框。 – wonko79 2013-05-07 14:18:45

+0

你可能也想看到这个如何做 - 我 - 覆盖-tostring-in-c-sharp-enums – nawfal 2013-06-08 22:25:12

回答

4

在你的第一次尝试,您可以用选择需要的项目:

comboBox1.SelectedItem = comboBox1.Items.Cast<ValueDisplayEnum>().First(x => (Enum1)x.Value == Enum1.MultiWordValue2); 
+0

Woa不错。更紧凑,并取代了我创建的模板化方法。太好了!谢谢你! – Jason 2013-05-07 16:43:09

+0

@ Lucky3 - 很棒的解决方案。我会补充一点,如果你试图将一个combobox数据绑定的值成员添加到数据表中,请进行编辑: combobox1.SelectedItem = combobox1.Items.Cast ()。首先(x => (字符串)(((DataRowView)x)[“ValueMemberColumnName”])== “DesiredValueMemberValue”); 也就是说,只要您的值成员列数据类型是字符串。否则,改变字符串转换,比较和文字。 – 2014-03-12 20:24:59

1

我喜欢以下解决方案,因为您在一个位置添加或更改枚举及其字符串值 - 在数组extendedEnumerations中。

public partial class Form1 : Form 
{ 
    public enum MyEnum 
    { 
     EnumValue1, 
     EnumValue2, 
     EnumValue3 
    } 

    public class EnumExtension 
    { 
     public MyEnum enumValue; 
     public String enumString; 
     public EnumExtension(MyEnum enumValue, String enumString) 
     { 
      this.enumValue = enumValue; 
      this.enumString = enumString; 
     } 
     public override string ToString() 
     { 
      return enumString; 
     } 
    } 

    private EnumExtension[] extendedEnumerations = 
    { 
     new EnumExtension(MyEnum.EnumValue1, "Enum Value 1"), 
     new EnumExtension(MyEnum.EnumValue2, "Enum Value 2"), 
     new EnumExtension(MyEnum.EnumValue3, "Enum Value 3"), 
    }; 

    public Form1() 
    { 
     InitializeComponent(); 
     foreach (EnumExtension nextEnum in extendedEnumerations) 
      comboBox1.Items.Add(nextEnum); 
     comboBox1.SelectedIndexChanged += new EventHandler(comboBox1_SelectedIndexChanged); 

     button1.Click += new EventHandler(button1_Click); 
     button2.Click += new EventHandler(button2_Click); 
     button3.Click += new EventHandler(button3_Click); 
    } 

    void button3_Click(object sender, EventArgs e) 
    { 
     comboBox1.SelectedItem = extendedEnumerations[2]; 
    } 

    void button2_Click(object sender, EventArgs e) 
    { 
     SetSelectedEnumeration(MyEnum.EnumValue2); 
    } 

    void button1_Click(object sender, EventArgs e) 
    { 
     SetSelectedEnumeration(MyEnum.EnumValue1); 
    } 

    private void SetSelectedEnumeration(MyEnum myEnum) 
    { 
     foreach (EnumExtension nextEnum in comboBox1.Items) 
     { 
      if (nextEnum.enumValue == myEnum) 
      { 
       comboBox1.SelectedItem = nextEnum; 
       break; 
      } 
     } 
    } 

    void comboBox1_SelectedIndexChanged(object sender, EventArgs e) 
    { 
     EnumExtension selectedExtension = (EnumExtension)comboBox1.SelectedItem; 
     MyEnum selectedValue = selectedExtension.enumValue; 
    } 
} 
+0

有趣。这比我期望的还要多一点样板(每个枚举类都有一个类),但我会试试看!也许给每个扩展类一个返回它可能的EnumExtension实例的静态成员会使它消耗很容易? – Jason 2013-05-07 15:12:24

+0

更多的数据,更少的代码(没有切换条款)。我总是喜欢维护数据数组的内容而不是修改代码。选择你的毒药。 – Mike 2013-05-07 15:16:36

+0

那么,foreach解决方案本身的做法,现在保持与我的枚举泛型ValueDisplayEnum类的技巧。我只是添加了你的SetSelectedEnumeration逻辑来找到正确的项目,并设置SelectedItem而不是SelectedValue,它的工作原理!所以谢谢你!这对我来说已经足够好了,但我肯定会将EnumExtension类视为一个单独而有趣的概念。 – Jason 2013-05-07 15:23:32

0

好设置的SelectedValue不工作,因为你有字符串数据,并希望与枚举值来选择它。

一个简单的解决办法是这样的:

private static void FillCombo(ComboBox box) 
     { 
      List<DisplayableStatus> ds = new List<DisplayableStatus>(); 
      foreach (var val in Enum.GetValues(typeof(Status))) 
       ds.Add(new DisplayableStatus { Status = (Status)val, DisplayText = val.ToString() + " Nice" }); 

      box.DataSource = ds; 
      box.DisplayMember = "DisplayText"; 
      box.ValueMember = "Status"; 

      box.SelectedValue = Status.Stop; 
     } 

     public enum Status 
     { 
      Unknown, 
      Start, 
      Stop 
     } 

     public class DisplayableStatus 
     { 
      public Status Status { get; set; } 
      public string DisplayText { get; set; } 
     } 

正如你可以看到你可以设置显示器和值成员单独的数据和观点。

+0

我不确定我是否同意我有comboBox Value的字符串数据。看看这一行:comboBox1.Items.Add(new ValueDisplayEnum(e,e.ToDisplayString()));我正在提供一个类作为comboBox项目。组合框的ValueMember是“Value”,它在对象上不是字符串。价值是枚举。但是,它看起来像是从类的列表中数据绑定组合框,而不是仅添加项目。我会试着看看它是否有效。 – Jason 2013-05-07 14:50:44

+0

没有你是对的。我监督了这一点。您的值是typeof对象,并且您希望将所选值设置为具有相同结果的typeof Enum1。使ValueDisplayEnum类型的Enum1,而不是对象,并在foreach中强制转换为Enum1。 – wonko79 2013-05-07 14:55:02

+0

其实,我试过(正如我在原来的帖子中提到的那样),它没有奏效。我将ValueDisplayEnum.Value属性设置为Enum1而不是对象,但它没有改变行为。 SelectedValue分配仍然无效。 – Jason 2013-05-07 15:10:01

3

使用Description属性上枚举字段,用于控制显示值在组合框中选择一个枚举一个通用的实用工具类。

/// <summary> 
    /// Return the contents of the enumeration as formatted for a combo box 
    /// relying on the Description attribute containing the display value 
    /// within the enum definition 
    /// </summary> 
    /// <typeparam name="T">The type of the enum being retrieved</typeparam> 
    /// <returns>The collection of enum values and description fields</returns> 
    public static ICollection<ComboBoxLoader<T>> GetEnumComboBox<T>() 
    { 
     ICollection<ComboBoxLoader<T>> result = new List<ComboBoxLoader<T>>(); 
     foreach (T e in Enum.GetValues(typeof(T))) 
     { 
      ComboBoxLoader<T> value = new ComboBoxLoader<T>(); 
      try 
      { 
       value.Display = GetDescription(e); 
      } 
      catch (NullReferenceException) 
      { 
       // This exception received when no Description attribute 
       // associated with Enum members 
       value.Display = e.ToString(); 
      } 
      value.Value = e; 
      result.Add(value); 
     } 
     return result; 
    } 

要在数据网格视图中使用它的窗体上,例如:

 DataGridViewComboBoxColumn trussLocationComboBoxColumn = trussLocationColumn as DataGridViewComboBoxColumn; 
     trussLocationComboBoxColumn.DataSource = EnumUtils.GetEnumComboBox<TrussLocationCase>(); 
     trussLocationComboBoxColumn.DisplayMember = "Display"; 
     trussLocationComboBoxColumn.ValueMember = "Value"; 

和对被拾取的显示字段

public enum TrussLocationCase 
{ 
    [Description("All Cases")] 
    AllCases, 
    [Description("1A")] 
    OneA, 
    [Description("1B")] 
    OneB, 
    [Description("2C")] 

枚举字段的说明并完成它的一切,ComboBoxLoader类

/// <summary> 
/// Class to provide assistance for separation of concern 
/// over contents of combo box where the 
/// displayed value does not match the ToString 
/// support. 
/// </summary> 
/// <typeparam name="T">The type of the value the combo box supports</typeparam> 
[DebuggerDisplay("ComboBoxLoader {Display} {Value.ToString()}")] 
public class ComboBoxLoader<T> 
{ 
    /// <summary> 
    /// The value to display in the combo box 
    /// </summary> 
    public string Display { get; set; } 

    /// <summary> 
    /// The actual object associated with the combo box item 
    /// </summary> 
    public T Value { get; set; } 
} 

会看到填充

All Cases 
1A 
1B 
2C 
... 
+0

这看起来像我最初尝试的枚举值/显示包装器的更健壮的版本。这不是我最终使用的,但如果我的声望足够高,我会完全+1。感谢您的建议,如果我的需求变得更加复杂,我一定会保持这种方便。 – Jason 2013-05-09 13:43:50

相关问题