将枚举类型绑定到WinForms组合框,然后进行设置

141

很多人已经回答了如何在WinForms中将枚举绑定到组合框的问题。它像这样:

comboBox1.DataSource = Enum.GetValues(typeof(MyEnum));

但是如果不能设置实际值来显示,那就没有多大用处了。

我尝试过:

comboBox1.SelectedItem = MyEnum.Something; // Does not work. SelectedItem remains null

我还尝试过:

comboBox1.SelectedIndex = Convert.ToInt32(MyEnum.Something); // ArgumentOutOfRangeException, SelectedIndex remains -1

有人有任何想法怎么做吗?


3
为什么不尝试使用 ComboBox.SelectedValue 呢? - Oliver Friedrich
6
如果你的问题已经得到回答,你应该选择一个答案。 - Ryan Leach
数据绑定枚举的目的并不是很清楚。枚举在运行时很可能不会改变。您还可以编写一个扩展方法,将组合框的项集合填充为枚举的所有值。 - Andreas
相关链接:https://dev59.com/L2035IYBdhLWcg3wE71s - JYelton
@OliverFriedrich 对我来说,SelectedValue 引发了一个 InvalidOperationException。"无法在具有空 ValueMemberListControl 中设置 SelectedValue。" - Tyler
28个回答

185

枚举类型

public enum Status { Active = 0, Canceled = 3 }; 

从中设置下拉框的值

cbStatus.DataSource = Enum.GetValues(typeof(Status));

从所选项目获取枚举

Status status; 
Enum.TryParse<Status>(cbStatus.SelectedValue.ToString(), out status); 

3
这恰恰是OP不想采用的方式。问题在于用户会看到每个值的代码名称,这种显示方式容易被重构,通常不太用户友好。 - Alejandro
1
这可以在C# 4.5及更高版本中进一步简化为:Enum.TryParse(cbStatus.SelectedValue.ToString(), out status); - TEK

54

简化方法:

首先,初始化该命令:(例如在 InitalizeComponent() 之后)

yourComboBox.DataSource =  Enum.GetValues(typeof(YourEnum));

要检索下拉框中选择的项目:

YourEnum enum = (YourEnum) yourComboBox.SelectedItem;
如果您想为下拉框设置值:
yourComboBox.SelectedItem = YourEnem.Foo;

3
只要“Display”值与“Value”成员相同,这个方法就有效,否则无效。 - Lord of Scripts
如何绑定到一个实例?假设枚举是类的成员?myCar.SpeedMode - joe

15

这段代码

comboBox1.SelectedItem = MyEnum.Something;

好的,问题可能在于DataBinding。DataBinding的赋值发生在构造函数之后,主要是第一次显示组合框时。请尝试在Load事件中设置该值。例如,添加以下代码:

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    comboBox1.SelectedItem = MyEnum.Something;
}

并检查是否正常工作。


14

尝试:

comboBox1.SelectedItem = MyEnum.Something;

编辑:

哎呀,你已经尝试过那个方法了。不过,当我的下拉框设置为“仅限下拉列表”时,它对我起作用。

这是完整的代码,对我来说有效(无论是下拉框还是仅限下拉列表):

public partial class Form1 : Form
{
    public enum BlahEnum
    { 
        Red,
        Green,
        Blue,
        Purple
    }

    public Form1()
    {
        InitializeComponent();

        comboBox1.DataSource = Enum.GetValues(typeof(BlahEnum));

    }

    private void button1_Click(object sender, EventArgs e)
    {
        comboBox1.SelectedItem = BlahEnum.Blue;
    }
}

有趣的是,你可以这样做 comboBox1.SelectedItem = BlahEnum.Blue; 但是如果你想让下拉框中的内容为字符串,例如下拉框中的一个选项为“可咀嚼维生素丸”呢? - barlop

13

假设你有以下的枚举类型

public enum Numbers {Zero = 0, One, Two};

你需要一个结构体将这些值映射到一个字符串:

public struct EntityName
{
    public Numbers _num;
    public string _caption;

    public EntityName(Numbers type, string caption)
    {
        _num = type;
        _caption = caption;
    }

    public Numbers GetNumber() 
    {
        return _num;
    }

    public override string ToString()
    {
        return _caption;
    }
}
现在返回一个对象数组,其中将所有的枚举映射为字符串:
public object[] GetNumberNameRange()
{
    return new object[]
    {
        new EntityName(Number.Zero, "Zero is chosen"),
        new EntityName(Number.One, "One is chosen"),
        new EntityName(Number.Two, "Two is chosen")
    };
}

使用以下内容填充您的组合框:

ComboBox numberCB = new ComboBox();
numberCB.Items.AddRange(GetNumberNameRange());
创建一个函数来获取枚举类型,以备将其传递给函数使用。
public Numbers GetConversionType() 
{
    EntityName type = (EntityName)numberComboBox.SelectedItem;
    return type.GetNumber();           
}

然后你应该没问题了 :)


+1 不错的解决方案。最近我也遇到了这个问题,并以类似的方式解决了它(只是用了一个“元组”而已)。我会将枚举值和描述都转换为属性,然后添加 numberCB.DisplayProperty = "Caption";numberCB.ValueProperty = "Num",这样你就可以直接使用 SelectedValue` 并绑定它了。 - Alejandro
在我看来,如果像添加“全部”/“全选”选项到ComboBox以过滤搜索中的所有行这样的功能,可能需要更完整的示例源代码。 - Kiquenet

9

在所有其他答复中可能看不到这个,但这是我想出来的代码,它有一个好处,即如果存在DescriptionAttribute,则使用该属性,否则使用枚举值本身的名称。

我使用字典是因为它具有现成的键/值项模式。一个 List<KeyValuePair<string,object>> 也可以工作,并且没有不必要的哈希,但字典使代码更清晰。

我获取具有 MemberTypeField 并且是字面值的成员。这将创建仅包含枚举值的序列。这很强大,因为枚举不能有其他字段。

public static class ControlExtensions
{
    public static void BindToEnum<TEnum>(this ComboBox comboBox)
    {
        var enumType = typeof(TEnum);

        var fields = enumType.GetMembers()
                              .OfType<FieldInfo>()
                              .Where(p => p.MemberType == MemberTypes.Field)
                              .Where(p => p.IsLiteral)
                              .ToList();

        var valuesByName = new Dictionary<string, object>();

        foreach (var field in fields)
        {
            var descriptionAttribute = field.GetCustomAttribute(typeof(DescriptionAttribute), false) as DescriptionAttribute;

            var value = (int)field.GetValue(null);
            var description = string.Empty;

            if (!string.IsNullOrEmpty(descriptionAttribute?.Description))
            {
                description = descriptionAttribute.Description;
            }
            else
            {
                description = field.Name;
            }

            valuesByName[description] = value;
        }

        comboBox.DataSource = valuesByName.ToList();
        comboBox.DisplayMember = "Key";
        comboBox.ValueMember = "Value";
    }


}

这是非常实用的答案!谢谢! - NoWar
兄弟,能不能投一百次赞啊?非常感谢。为了方便大家的工作,也许你还需要一个控制扩展来将枚举值绑定到GroupBox中的RadioButton - Marcelo Scofano Diniz

8

试试这个:

// fill list
MyEnumDropDownList.DataSource = Enum.GetValues(typeof(MyEnum));

// binding
MyEnumDropDownList.DataBindings.Add(new Binding("SelectedValue", StoreObject, "StoreObjectMyEnumField"));

StoreObject 是我的一个对象示例,其中包含 StoreObjectMyEnumField 属性,用于存储 MyEnum 值。


2
这绝对是最好的方法,但实际上它对我没用。我不得不使用“SelectedItem”而不是“SelectedValue”。 - Tiago Freitas Leal

5
 public static void FillByEnumOrderByNumber<TEnum>(this System.Windows.Forms.ListControl ctrl, TEnum enum1, bool showValueInDisplay = true) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum) throw new ArgumentException("An Enumeration type is required.", "enumObj");

        var values = from TEnum enumValue in Enum.GetValues(typeof(TEnum))
                     select
                        new
                         KeyValuePair<TEnum, string>(   (enumValue), enumValue.ToString());

        ctrl.DataSource = values
            .OrderBy(x => x.Key)

            .ToList();

        ctrl.DisplayMember = "Value";
        ctrl.ValueMember = "Key";

        ctrl.SelectedValue = enum1;
    }
    public static void  FillByEnumOrderByName<TEnum>(this System.Windows.Forms.ListControl ctrl, TEnum enum1, bool showValueInDisplay = true  ) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum) throw new ArgumentException("An Enumeration type is required.", "enumObj");

        var values = from TEnum enumValue in Enum.GetValues(typeof(TEnum))
                     select 
                        new 
                         KeyValuePair<TEnum,string> ( (enumValue),  enumValue.ToString()  );

        ctrl.DataSource = values
            .OrderBy(x=>x.Value)
            .ToList();

        ctrl.DisplayMember = "Value";
        ctrl.ValueMember = "Key";

        ctrl.SelectedValue = enum1;
    }

你是什么意思?我不明白你的评论。这个扩展方法有效。 - Mickey Perlstein
这取决于您的枚举数字是否允许 OR 标志。如果是这样,您可以添加一个名为 All 的标志,其值为 255,并使用 All 作为 enum1 调用函数,从而创建默认值。例如: comboBox1.FillByEnumOrderByName(MyEnum.All) - Mickey Perlstein
有这样的选项:var l = values.OrderBy(x=>x.Value).ToList(); l.Insert(0, "All"); - Kiquenet
我的枚举是枚举A { duck = 0, swan = 1, joker = 3 }; 你的系统对我的情况不适用。 - Mickey Perlstein

4
这对我有用:
comboBox1.DataSource = Enum.GetValues(typeof(MyEnum));
comboBox1.SelectedIndex = comboBox1.FindStringExact(MyEnum.something.ToString());

这是解决问题最简单、最优雅的方法。只需确保您的 My.Settings.Type 设置为枚举类型即可。 - stigzler
更简单的方法是:comboBox1.SelectedItem = MyEnum.something; 也可以。 - mBardos

3

根据@Amir Shenouda的回答,我得到了以下内容:

枚举定义:

public enum Status { Active = 0, Canceled = 3 }; 

从中设置下拉列表的值:

cbStatus.DataSource = Enum.GetValues(typeof(Status));

从所选项目中获取枚举值:

Status? status = cbStatus.SelectedValue as Status?;

2
为什么要使用可空类型?你可以使用显式转换(括号转换)而不使用可空类型。 - John Demetriou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接