【问题标题】:Selecting ComboBox by ValueMember for Enum Proprety of Class为类的枚举属性按值成员选择组合框
【发布时间】:2013-05-01 10:56:15
【问题描述】:

我已经阅读了很多关于 Winforms ComboBox ValueMember 属性主题的有用帖子,但没有一个回答我的具体问题。首先,我将描述一个运行良好的简单示例,然后我将描述我想如何更改它,并询问如何做到这一点(我还将提供我的一个尝试)。

首先,工作示例。只是一个带有组合框控件的表单,以及这段代码:

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;
        }
    }
}

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

以下是我的第一次尝试。我创建了一个简单的类来包装枚举值和显示字符串,并将组合框 DisplayMember 和 ValueMember 设置为新类的属性。这部分有效;组合框的项目使用显示值正确填充,但我无法设置 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);

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

【问题讨论】:

  • 设置 SelectedValue 不起作用,因为您有字符串数据并希望使用枚举值选择它。
  • 你可能也想看看这个 how-do-i-override-tostring-in-c-sharp-enums

标签: c# winforms combobox enums selectedvalue


【解决方案1】:

在您的第一次尝试中,您可以选择所需的项目:

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

【讨论】:

  • 哇很好。更紧凑,并替换了我创建的模板化方法。好的!谢谢!
  • @Lucky3 - 很好的解决方案。我将补充一点,如果您尝试将组合框数据绑定到数据表的值成员,请进行此编辑:combobox1.SelectedItem = combobox1.Items.Cast().First(x => (string )(((DataRowView)x)["ValueMemberColumnName"]) == "DesiredValueMemberValue");也就是说,如果您的值成员列数据类型是字符串。否则,更改字符串转换、比较和文字。
【解决方案2】:

一个通用实用程序类,它使用枚举字段上的描述属性来控制组合框中枚举的显示值。

    /// <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
...

【讨论】:

  • 这看起来像是我最初尝试的枚举值/显示包装器的一个更健壮的版本。这不是我最终使用的,但如果我的声誉足够高,我会完全 +1!感谢您的建议,如果我的需求变得更复杂,我一定会保留它。
【解决方案3】:

我喜欢以下解决方案,因为您在一个地方添加或更改枚举及其字符串值 - 在数组 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;
    }
}

【讨论】:

  • 有趣。这比我希望的要多一点样板(每个枚举都有一个类),但我会试一试!也许给每个扩展类一个返回其可能的 EnumExtension 实例的静态成员会使其易于使用?
  • 更多数据,更少代码(无 switch 子句)。我总是更喜欢维护数据数组的内容,而不是修改代码。选择你的毒药。
  • 好吧,foreach 的赋值解决方案现在可以解决问题,与我的枚举通用 ValueDisplayEnum 类保持一致。我刚刚添加了您的 SetSelectedEnumeration 逻辑以找到正确的项目并设置 SelectedItem 而不是 SelectedValue,它可以工作!所以谢谢你!目前这对我来说已经足够好了,但我肯定会将 EnumExtension 类视为一个独立且有趣的概念。
【解决方案4】:

设置 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; }
        }

如您所见,您可以设置显示和值成员来分隔数据和视图。

【讨论】:

  • 我不确定我是否同意我有组合框值的字符串数据。看看这一行:comboBox1.Items.Add(new ValueDisplayEnum(e, e.ToDisplayString()));我提供一个类作为组合框项目。组合框的 ValueMember 是“Value”,它在对象上不是字符串。值是枚举。但是,看起来您正在从类列表中对组合框进行数据绑定,而不仅仅是添加项目。我会试试看它是否有效。
  • 不,你说得对。我已经监督了这一点。您的值是 typeof 对象,并且您希望将所选值设置为 typeof Enum1 并具有相同的结果。使 ValueDisplayEnum 类型为 Enum1 而不是对象,并在 foreach 中强制转换为 Enum1。
  • 实际上,我尝试过(如我原来的帖子中所述),但没有成功。我将 ValueDisplayEnum.Value 属性设为 Enum1 而不是对象,但它并没有改变行为。 SelectedValue 分配仍然无效。
猜你喜欢
  • 2012-08-19
  • 2021-03-13
  • 1970-01-01
  • 2011-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
相关资源
最近更新 更多