Loading

Checked combo box of bit flags

I've had this situation turn up a couple of times. I have a set of flags that I want the user to pick from. They can select any combination of flags. All I want is a single int value I can save to or load from a database. Step in the CheckedComboBoxEdit.

Lets assume a fairly simple flag based enumeration type:

    [Flags]
    public enum TestSwitches : int
    {
        Item1 = 1,
        Item2 = 2,
        Item3 = 4,
        Item4 = 8,
        Item5 = 16,
        Item6 = 32,
        Item7 = 64,
        Item8 = 128,
    }

So, I have eight items with utterly meaningless names. Yours will be far more descriptive, of course. What we're aiming for is a combo box which, when expanded out, looks like this:

Combo box with flags as options.

When the combo is closed, we want the user to see comma separated list of the options they have selected.

Values displayed when the combo is closed.

Populating the combo

So, the first task at hand is to populate the combo box. We need one entry per enumeration item:

cboOptions.Properties.Items.AddRange(Enum.GetValues(typeof(TestSwitches))
    .Cast<TestSwitches>()
    .Where(item => item > 0)
    .Select(value => new CheckBoxItems(value.ToString(), value))
    .OrderBy(item => item.Value)
    .ToArray());

The trick is to read it through slowly. So, the call to Enum.GetValues will return us a list of the flag values. We need to cast this to the enum name as Linq will not know how to interpret the flags. The select then creates an instance of CheckBoxItems for each of the flags and adds this to the combo.

Assuming we have no start set of flags, this is all we need to do. However, it is much more likely that we will have a set of flags that we need to put in to the combo box...

Setting values in code.

Ok, this routine goes through each item in the drop down list and checks whether the corresponding item has a flag set in the starter value. If it does, we set the corresponding value in the drop down list, otherwise, we unset it.

private void SetDropdownFlags(TestSwitches testValue)
{
    foreach (CheckedListBoxItem item in cboOptions.Properties.Items)
    {
        var itemValue = item.Value as CheckBoxItems;

        item.CheckState = (testValue.HasFlag(itemValue.Value))
                ? CheckState.Checked
                : CheckState.Unchecked;
    }
}

The final thing we need to do is get the value out of the combo and back to an integer that we can store away.

Reading the value back

Again, a pretty straight forward task with a little linq.

TestSwitches newValue = (from CheckedListBoxItem item
            in cboOptions.Properties.Items
            where item.CheckState == CheckState.Checked
            select item.Value as CheckBoxItems)
            .Aggregate<CheckBoxItems, TestSwitches>(0, (current, itemValue) => current | itemValue.Value);

This is going to extract all of the checked items from the combo and construct a new value with all of the corresponding flags set.

And now, with descriptions

To complicate things a little, I want to control what the user sees for each of these flags. The enumeration names could be a little obscure, so it would be good to have a description in the drop down. So, what does my new flags enumeration look like:

    [Flags]
    public enum TestSwitches : int
    {
        [Description("This is item 1")]
        Item1 = 1,
        [Description("This is item 2")]
        Item2 = 2,
        [Description("This is item 3")]
        Item3 = 4,
        [Description("This is item 4")]
        Item4 = 8,
        [Description("This is item 5")]
        Item5 = 16,
        [Description("This is item 6")]
        Item6 = 32,
        [Description("This is item 7")]
        Item7 = 64,
        [Description("This is item 8")]
        Item8 = 128,
    }

So, I have eight items with utterly meaningless names. These have been augmented with descriptions that the user will find useful. What we're aiming for is a combo box which, when expanded out, looks like this:

Combo box with flags as options.

When the combo is closed, we want the user to see comma separated list of the options they have selected.

Values displayed when the combo is closed.

So, the first task at hand is to populate the combo box. We need one entry per enumeration item, but we want to display the description text rather than the enumerated value. Linq has the answer for us here, though it does, at first glance, appear a little complicated:

    var itemList = Enum.GetValues(typeof(TestSwitches))
        .Cast<TestSwitches>()
        .Select(value =>
        {
            var descriptionAttribute = Attribute
                        .GetCustomAttribute(value.GetType()
                        .GetField(value.ToString()),
                    typeof(DescriptionAttribute)) as DescriptionAttribute;
            return descriptionAttribute != null ? new CheckBoxItems
            (
                    descriptionAttribute.Description,
                    value
            ) : null;
        })
        .OrderBy(item => item.Value)
        .ToList();

    foreach (var value in itemList)
    {
        cboOptions.Properties.Items.Add(new CheckedListBoxItem(value));
    }

The trick is to read it through slowly. So, the call to Enum.GetValues will return us a list of the flag values. We need to cast this to the enum name as Linq will not know how to interpret the flags. For each flag, the select fires off a lambda expression that retrieves the value of the Description attribute associated with the flag. Having retrieved the description, we create a new instance of the CheckBoxItems class which we populate with the description of the flag and the value of the flag. The resulting list of check box items is then added to the combo box.

Setting the flag values and reading them back doesn't change. The only code we needed to change was the code to populate the combo box.