Enumeration list box


As the name suggests, the enumeration list box is a list box which shows and sets enumeration values using radio buttons. But this post is not really about the ListBox or RadioButton. It about how to populate a list box (displaying radio buttons or pictures of cherubs) from an enumeration. There’s a project to accommpany this post which show all the stuff reviewed here. The project is more complicated that it probably should be because it’s also a template for me of an MEF enabled MVVM application which is able to automatically save and load selected properties and also happens to show how a list box can represent the state of an enumeration.

In Xaml environments I’ve found blocks of radio buttons difficult to deal with. Judging from blog posts I’m not alone. The problem seems to be the reliability with which dependency properties attached to radio buttons are fired and how reliably the initial state of a button group can be established programmatically. There’s no problem when attaching a click event to the buttons and handing the event in code behind. It’s when using MVVM and expecting the button state changes to update properties on the view model that the problem occurs.

One useful suggestion I’ve seen is to use a ListBox styled to display a set of radio buttons. The sematics of a ListBox working in single item selection mode and a group of radio buttons is the same. This post shows how to style a ListBox to display radio buttons and works well.

But why radio buttons? The intention has been to display the status of a property of an enumeration type and a set of RadioButtons is a fairly standard way to show this state and change it. A positive effect of using the ListBox instead of using multiple RadioButtons is that the SelectedItemChanged event can be used to update the enumeration value of the property.

Of course the ListBox could be hard coded. But where would the fun be in that. Much better to generate the ItemsSource of the ListBox dynamically. This has several advantages. Specifically:

  • Some useful text can be generated for each item of an enumeration rather than presenting the enumeration value text
  • The list can be changed dynamically in order to respond to changes in the UI or for other reasons
  • The displayed list will change automatically as new items are added to/removed from the underlying enumeration over the life cycle of the application

Below I’ve shown one of my enumerations. As you can see, I’ve taken the opportunity to decorate each item in the enumeration with a description. I’ve shown the displayed text the in the example but in the project the “description” is really the name of a translatable resource property which is resovled at run-time.

I’ve also created a custom attribute (ItemTypeAttribute) and a second enumeration (ItemType) which will provide some filtering. In this case, provide a mechanism to distinguish between the standard and extended options represented in the Items enumeration. This can then be used to change the display (see the screen shot).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum Items
{
  [ItemType(ItemType = ItemType.Standard | ItemType.Extended)]
  [Description("First standard item")]
  ItemOne,
  [ItemType(ItemType = ItemType.Standard | ItemType.Extended)]
  [Description("Second standard item")]
  ItemTwo,
  [ItemType(ItemType = ItemType.Standard | ItemType.Extended)]
  [Description("Third standard item")]
  ItemThree,
  [Description("First extended item")]
  [ItemType(ItemType = ItemType.Extended)]
  ItemFour,
  [ItemType(ItemType = ItemType.Extended)]
  [Description("First extended item")]
  ItemFive,
  [ItemType(ItemType = ItemType.Extended)]
  [Description("First extended item")]
  ItemSix,
}
public enum Items
{
  [ItemType(ItemType = ItemType.Standard | ItemType.Extended)]
  [Description("First standard item")]
  ItemOne,
  [ItemType(ItemType = ItemType.Standard | ItemType.Extended)]
  [Description("Second standard item")]
  ItemTwo,
  [ItemType(ItemType = ItemType.Standard | ItemType.Extended)]
  [Description("Third standard item")]
  ItemThree,
  [Description("First extended item")]
  [ItemType(ItemType = ItemType.Extended)]
  ItemFour,
  [ItemType(ItemType = ItemType.Extended)]
  [Description("First extended item")]
  ItemFive,
  [ItemType(ItemType = ItemType.Extended)]
  [Description("First extended item")]
  ItemSix,
}

I use Caliburn as an MVVM framework. Caliburn automatically associates and binds the Xaml view and the class which implements the view model based on a naming convention and makes the view model the DataContext of the view. So I can define two properties on the page to hold the state of the list boxes:

1
2
3
4
5
public ItemType ItemType
{ get; set; }
 
public Items Items
{ get; set; }
public ItemType ItemType
{ get; set; }

public Items Items
{ get; set; }

These are then bound to the the SelectedValue property of the respective list box. Nothing magical here though the actual implementation is a bit more complicated than shown here so the list displayed by the Items listbox can be changed depending upon the value of the ItemType property.

The view model also has a couple of properties (one for each list box) to provide the ItemsSource content. Again nothing magical. These properties are bound to the ItemsSource properties of the respective list box. Here’s one of them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public object ItemsList
{
  get
  {
    try
    {
      return CreateVerticalList
        <Items, Properties.Resource, 
          ItemTypeAttribute>(
            (item, actionToApply) => (actionToApply.ItemType & this.ItemType) != 0);
    }
    catch (Exception ex)
    {
      Log.Error(ex);
    }
    return null;
  }
}
public object ItemsList
{
  get
  {
    try
    {
      return CreateVerticalList
        <Items, Properties.Resource, 
          ItemTypeAttribute>(
            (item, actionToApply) => (actionToApply.ItemType & this.ItemType) != 0);
    }
    catch (Exception ex)
    {
      Log.Error(ex);
    }
    return null;
  }
}

Mmm. So what’s that function CreateVerticalList<>? Glad you asked. This is a generic function to return a list of Key/Value pairs holding an enumeration value and corresponding description which can be passed directly to the ListBox’s ItemsSource for any enumeration. The only requirement is that the ListBox’s SelectedValuePath property is set to “Key” and the DisplayMemberPath property is set to “Value”. With these bits in place, the ListBox comes to life and selections automatically update the properties with enumerated types.

So what does the CreateVerticalList<> do? Here’s it’s implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private IEnumerable<KeyValuePair<TE, string>> 
    CreateVerticalList<TE, TD, TA>(Func<TE, TA, bool> callback)
{
  BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
 
  return from f in typeof(TE).GetFields()
      where !f.IsSpecialName
      let attr = f.GetAttributes<DescriptionAttribute>(false).FirstOrDefault()
      where attr != null
      let actionToApply = f.GetAttributes<TA>(false).FirstOrDefault()
      let item = (TE)f.GetValue(f)
      where (actionToApply == null || callback(item, actionToApply))
      let pi = typeof(TD).GetProperty(attr.Description, flags)
      let desc = pi == null ? attr.Description : pi.GetValue(null, null).ToString()
      select new KeyValuePair<TE, string>((TE)f.GetValue(f), desc);
}
private IEnumerable<KeyValuePair<TE, string>> 
    CreateVerticalList<TE, TD, TA>(Func<TE, TA, bool> callback)
{
  BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

  return from f in typeof(TE).GetFields()
      where !f.IsSpecialName
      let attr = f.GetAttributes<DescriptionAttribute>(false).FirstOrDefault()
      where attr != null
      let actionToApply = f.GetAttributes<TA>(false).FirstOrDefault()
      let item = (TE)f.GetValue(f)
      where (actionToApply == null || callback(item, actionToApply))
      let pi = typeof(TD).GetProperty(attr.Description, flags)
      let desc = pi == null ? attr.Description : pi.GetValue(null, null).ToString()
      select new KeyValuePair<TE, string>((TE)f.GetValue(f), desc);
}

The function takes three types: TE is the enumeration type to use; TD is the type where the static properties providing string resources can be found (for example, Properties.Resources); and TA is a filtering attribute. It also takes a single argument which is a function that allows a caller to define whether or not an enumeration item should be included in the output.

The function begins by grabbing the items of the enumeration and a DescriptionAttribute if it exists. It next looks for a ‘filter’ attribute and retrieves the underlying value of the item. Finally it creates the KeyValuePair.

In .NET 4.0 the use of the KeyValuePair<TE,string> type can be replaced with an anonymous type. It works just as well. However unlike .NET 4.0, .NET 3.5 does not allow for a return type of IEnumerable<object>. In .NET 3.5 the query can return an anonymous type but the function return value must then be object in which case the caller can do little with the result except enumerate the list which limits the ability to reuse the method.

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

Be the first to leave a comment!