0
Reply

Binding combobox to an enum that contains string descriptor attributes

Brian

Brian

Aug 3 2011 4:40 PM
2k
I'm trying to get a better understanding of databinding and the bindingsource component so I've created a a simple test app.  Here's what I've done so far:

I created a data class like this to represent an object with various properties:


namespace DataBindingTest
{
    public enum stringEnum
    {
        Red,
        Green,
        Yellow,
        Blue
    }


    public enum stringEnumWithDescriptionAttributes
    {
        [Description("New York City")]
        NewYorkCity,
        [Description("Los Angeles")]
        LosAngeles,
        Washington,
        [Description("San Antonio")]
        SanAntonio,
        Chicago
    }


    class DataObject
    {
        public string StringProperty { get; set; }
        public int IntProperty { get; set; }
        public bool BoolProperty { get; set; }


        public stringEnum StringEnumProperty { get; set; }


        public string StringEnumPropertyString
        {
            get { return StringEnumProperty.ToString(); }
            set { StringEnumProperty = (stringEnum)Enum.Parse(typeof(stringEnum), value); }
        }


        public stringEnumWithDescriptionAttributes stringEnumWithDescriptionAttributesProperty { get; set; }


        public string stringEnumWithDescriptionAttributesPropertyString
        {
            get { return stringEnumWithDescriptionAttributesProperty.ToString(); }
            set { stringEnumWithDescriptionAttributesProperty = (stringEnumWithDescriptionAttributes)Enum.Parse(typeof(stringEnumWithDescriptionAttributes), value); }
        }
    }
}

Then I added a comboBox (comboBox1), a NumericUpDown control, a checkBox and two more comboBoxes (comboBox2 and comboBox3) to my form.  Next, I created a new DataSource from my DataOject class declared above and added a BindingSource compoent to my form.  I set the DataSource property of the BindingSource to the DataSource that I created. Then I Set the DataSource for comboBox1 to the BindingSource and the DisplayMember to the Name property of the BindingSource.  I have bound the IntProperty of the BindingSource to a NumericUpDown control and the BoolProperty of the BindingSource to a checkBox control.  In my form constructor, I have created an array of 3 DataObjects, defined all the properties and then set the DataSource property of the BindingSource to this array.

My main form looks like this:

        // create array
        DataObject[] arrayOfObjects = new DataObject[3];


        public Form1()
        {
            InitializeComponent();


            arrayOfObjects[0] = new DataObject();
            arrayOfObjects[0].StringProperty = "String #1";
            arrayOfObjects[0].IntProperty = 35;
            arrayOfObjects[0].StringEnumProperty = stringEnum.Blue;
            arrayOfObjects[0].stringEnumWithDescriptionAttributesProperty = stringEnumWithDescriptionAttributes.NewYorkCity;
            arrayOfObjects[0].BoolProperty = true;


            arrayOfObjects[1] = new DataObject();
            arrayOfObjects[1].StringProperty = "String #2";
            arrayOfObjects[1].IntProperty = 47;
            arrayOfObjects[1].StringEnumProperty = stringEnum.Green;
            arrayOfObjects[1].stringEnumWithDescriptionAttributesProperty = stringEnumWithDescriptionAttributes.Chicago;
            arrayOfObjects[1].BoolProperty = false;


            arrayOfObjects[2] = new DataObject();
            arrayOfObjects[2].StringProperty = "String #3";
            arrayOfObjects[2].IntProperty = 24;
            arrayOfObjects[2].StringEnumProperty = stringEnum.Red;
            arrayOfObjects[2].stringEnumWithDescriptionAttributesProperty = stringEnumWithDescriptionAttributes.LosAngeles;
            arrayOfObjects[2].BoolProperty = true;


            // set datasource of bindingsource to the array
            bindingSource1.DataSource = arrayOfObjects;


            // bind comboBox2 datasource to the string enumeration in the DataObject class
            comboBox2.DataSource = Enum.GetNames(typeof(stringEnum));

    // bind combobox3 datasource to the string enumeration with description attributes in the DataObject class

            comboBox3.DataSource = Enum.GetNames(typeof(stringEnumWithDescriptionAttributes));
        }

I also have a couple of static helper classes that look like this:

public static string GetEnumDescription(Enum value)
{
    FieldInfo fi = value.GetType().GetField(value.ToString());


    DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);


    if (attributes != null && attributes.Length > 0)
    {
        return attributes[0].Description;
    }
    else
    {
        return value.ToString();
    }
}


public static IList ToList(this Type type)
{
    ArrayList list = new ArrayList();
    Array enumValues = Enum.GetValues(type);


    foreach (Enum value in enumValues)
    {
        list.Add(new KeyValuePair(value, GetEnumDescription(value)));
        //list.Add(GetEnumDescription(value));
    }


    return list;
}

What I'm trying to accomplish here is to select an item from comboBox1 and have the values of its properties displayed in the various controls.  For example, if I choose "String #1" from comboBox1, I want to see its IntProperty bound to and displayed in my NumericUpDown control.  I want to see its BoolProperty bound to and displayed in my checkbox control.  And finally, I'd like to see its StringEnumProperty bound to and displayed in comboBox2.  So far this is all working great.  The problem, however, is that I have some enum values that need to contain spaces in the name so I created stringEnumWithDescriptionAttributes and setup a new property of that type in my DataObject class.  As you can see in the above code, this statement:

            // bind combobox3 datasource to the string enumeration with description attributes in the DataObject class

            comboBox3.DataSource = Enum.GetNames(typeof(stringEnumWithDescriptionAttributes)); // <- this causes the actual value in the enum to be displayed in the combobox instead of the string descriptors

results in the actual enum value being displayed in the comboBox instead of the corresponding string description (so I'm seeing "NewYorkCity" instead of "New York City").  I was able to fix that problem by changing the previous statement to:

            comboBox3.DataSource = new BindingSource(Utility.ToList(typeof(stringEnumWithDescriptionAttributes)), null);
            comboBox3.DisplayMember = "Value";
            comboBox3.ValueMember = "Key";

Now the string descriptions are being displayed properly in the comboBox.  Unfortunately, when I select a new value in comboBox1 and the app tries to set stringEnumWithDescriptionAttributesPropertyString with the selected value in comboBox3, I get the following exception:

 Requested value '[NewYorkCity, New York City]' was not found.

Based on that, it looks like stringEnumWithDescriptionAttributesPropertyString is being set to the KeyPairValue from the comboBox instead of just the enum value.  I've been banging my head against this for a while now and haven't made any progress.  Can anyone help me understand what's happening here and what I can to fix it?  Thank you very much!