简体   繁体   English

使用带有枚举项的c#winforms数据绑定时,如何在组合框中获得友好的文本描述?

[英]How can I get friendly text descriptions in a combo box when using c# winforms data binding with enum items?

I have several ComboBox controls, with DropDownStyle set to DropDownList. 我有几个ComboBox控件,其中DropDownStyle设置为DropDownList。 The items that are selected are enum values, but I want "friendly" descriptions of these to be displayed, ie with spaces and not camel case. 选择的项目是枚举值,但我希望显示这些值的“友好”描述,即带空格而不是驼峰式大小写。

I want the combo box to be "two-way" bound to the data object: if the data object changes its property it alters the combo box, and vice versa. 我希望组合框被“双向”绑定到数据对象:如果数据对象更改其属性,它将更改组合框,反之亦然。

I can do this easily with strings but the problem is I am binding the controls to enum properties in the objects, so I want the Items collection of the Combo Box to contain actual enums. 我可以使用字符串轻松地做到这一点,但是问题是我将控件绑定到对象中的枚举属性,因此我希望组合框的Items集合包含实际枚举。 This obviously isn't going to work. 这显然是行不通的。

Solution 1: textual properties 解决方案1:文字属性

I can create extra properties in my object which are textual, and map those to the enum values. 我可以在对象中创建文本属性,然后将其映射到枚举值。 This is a bit messy though as I am including things in my business logic layer which really belong in the UI. 但是,这有点混乱,因为我在业务逻辑层中包含了真正属于UI的内容。 In that business logic layer they should be enums and not strings. 在该业务逻辑层中,它们应该是枚举而不是字符串。

Solution 2: event handlers 解决方案2:事件处理常式

Another alternative is to use event handlers so that when the user changes the option it gets the selected item text and finds the appropriate enum value, then sets this in the object. 另一种选择是使用事件处理程序,以便当用户更改选项时,它会获取选定的项目文本并找到适当的枚举值,然后在对象中进行设置。 This is only one way binding though. 这只是绑定的一种方式。

Attempted Solution 3 尝试解决方案3

public class BusinessObject
{
    private NumberCategory category;

    public NumberCategory Category
    {
        get
        {
            return category;
        }
        set
        {
            category = value;
        }
    }
}

public enum NumberCategory
{
    [Description("Negative Number")]
    NegativeNumber,

    [Description("Zero")]
    Zero,

    [Description("One")]
    One,

    [Description("Prime Number")]
    PrimeNumber,

    [Description("Composite Number")]
    CompositeNumber,
}

public class EnumDescriptionAdapter
{
    private readonly BusinessObject businessObject;

    public EnumDescriptionAdapter(BusinessObject businessObject)
    {
        this.businessObject = businessObject;
    }

    public string CategoryValue
    {
        get
        {
           //get the enum from businessObject and convert to a string
            return EnumUtils.GetDescription(businessObject.Category);
        }

        set
        {
            //get the string, convert to an enum and set it in BusinessObject
            businessObject.Category = EnumUtils.GetValueFromDescription<NumberCategory>(value); 
        }
    }
}

public static class EnumUtils 
{

    public static T GetValueFromDescription<T>(string description)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();
        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (attribute != null)
            {
                if (attribute.Description == description)
                    return (T)field.GetValue(null);
            }
            else
            {
                if (field.Name == description)
                    return (T)field.GetValue(null);
            }
        }
        throw new ArgumentException("Not found.", "description");
        // or return default(T);
    }

    public static string GetDescription(Enum value)
    {
        Type type = value.GetType();
        string name = Enum.GetName(type, value);
        if (name != null)
        {
            FieldInfo field = type.GetField(name);
            if (field != null)
            {
                DescriptionAttribute attr =
                       Attribute.GetCustomAttribute(field,
                         typeof(DescriptionAttribute)) as DescriptionAttribute;
                if (attr != null)
                {
                    return attr.Description;
                }
            }
        }
        return null;
    }
}

public partial class Form1 : Form
{
    public BusinessObject businessObject;

    public Form1()
    {
        InitializeComponent();

        string[] descriptions = Enum.GetValues(typeof(NumberCategory)).Cast<NumberCategory>().Select(e => EnumUtils.GetDescription(e)).ToArray();

        comboBox1.DataSource = descriptions;

        businessObject = new BusinessObject();
        EnumDescriptionAdapter adapter = new EnumDescriptionAdapter(businessObject);

        comboBox1.DataBindings.Add(new Binding("SelectedItem", adapter, "CategoryValue"));
    }

    private void button1_Click(object sender, EventArgs e)
    {
        businessObject.Category = NumberCategory.PrimeNumber;
    }
}

I put a button ( button1 ) and a combo box ( comboBox1 ) on my form. 我在表单上放置了一个按钮( button1 )和一个组合框( comboBox1 )。 When I change the combo box selected item, it does fire the setter in EnumDescriptionAdapter.CategoryValue and change the businessObject . 当我更改组合框选定的项目时,它确实会触发EnumDescriptionAdapter.CategoryValue的setter并更改businessObject However, the reverse is not true: if I press the button it changes the businessObject but doesn't alter the selected item in comboBox1 . 但是,事实并非如此:如果按下按钮,它将更改businessObject但不会更改comboBox1的选定项。

I don't know if it is more elegant but you can create a class with 2 properties, one for showing and one as value. 我不知道它是否更优雅,但是您可以创建一个具有2个属性的类,一个用于显示,另一个作为值。 You then populate a list of such created, one way or another, from your enums. 然后,从枚举中以一种或另一种方式填充此类创建的列表。

You get a star for recognising that decorating business layer stuff with presentation layer stuff is not considered good practice. 您会认识到用表示层材料装饰业务层材料被认为不是好习惯,这让您大为赞赏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM