Thursday, April 8, 2010

Use Enum as ItemsSource

Imagine we have an Enum type and we want to use that Enum type to be an ItemsSource of ListBox or ComboBox. Bolow is an example of an Enum type called AirFareBookingStatus

   1: public enum AirFareBookingStatus
   2: {
   3:     AlreadyPaid,
   4:     Cancelled,
   5:     Delayed,
   6:     Deleted,
   7:     InProcessing,
   8:     New
   9: }

What we want to achieve is to populate all the statuses from the Enum to a ListBox or ComboBox.

There is already a solution for this from the Silverlight forum. The idea is to go through all the Field and put them into a IEnumerable of KeyValuepair

   1: IEnumerable<KeyValuePair<string, AirFareBookingStatus>> results; 
   2: var x = typeof(AirFareBookingStatus).GetFields().Where(info => info.FieldType.Equals(typeof(AirFareBookingStatus)));
   3: results = from field in x
   4:           select new KeyValuePair<string, AirFareBookingStatus>(field.Name, (AirFareBookingStatus) Enum.Parse(typeof(AirFareBookingStatus), field.Name, false)); 

Then all we have to do is assign the result to ListBox or ComboBox ItemsSource

   1: lstStatus.ItemsSource = results;
   2: cmbStatus.ItemsSource = results;

That would be good enough for it to work.

But we can even make it more generic by creating a helper function as below

   1: public static class Helper
   2: {
   3:     internal static IEnumerable<KeyValuePair<string, T>> GetEnumList<T>()
   4:     {
   5:         var x = typeof(T).GetFields().Where(info => info.FieldType.Equals(typeof(T)));
   6:         return from field in x
   7:                select new KeyValuePair<string, T>(field.Name, (T) Enum.Parse(typeof(T), field.Name, false)); 
   8:     }
   9: }

Then, we can call that function for every Enum type we have. For example

   1: lstStatus.ItemsSource = Helper.GetEnumList<AirFareBookingStatus>();
   2: cmbColors.ItemsSource = Helper.GetEnumList<ColorEnum>();

There is still one problem left we have. The item still displays the whole object, so make it display correctly, we need to create DataTemplate resource

   1: <DataTemplate x:Key="KeyValuePairDataTemplate">
   2:     <TextBlock Text="{Binding Key}" />
   3: </DataTemplate>

Then apply that DataTemplate to the ItemTemplate of ListBox or ComboBox as below

   1: <ListBox x:Name="lstStatus"
   2:     ItemTemplate="{StaticResource KeyValuePairDataTemplate}" />
   4: <ComboBox x:Name="cmbColors"
   5:     ItemTemplate="{StaticResource KeyValuePairDataTemplate}" />

That’s it. Hope that helps.

Continue …

Now, I have run into another problem. Doing that way above is all good, except the name won’t be as flexible. So, I am going to Description attribute to display data instead of the normal name of the Enum. To do that, I’ve changed a bit of my Enum like below

   1: public enum AirFareBookingStatus
   2: {
   3:     [Description("Already Paid")]
   4:     AlreadyPaid,
   5:     [Description("i am cancelled")]
   6:     Cancelled,
   7:     [Description("I got delayed")]
   8:     Delayed,
   9:     [Description("oh man i am about to be deleted")]
  10:     Deleted,
  11:     [Description("who is processing me")]
  12:     InProcessing,
  13:     [Description("I am brand new babe")]
  14:     New
  15: }

Next, I changed a bit in my helper function as well. It’ll get the Description attribute and make it to be Key

   1: internal static IEnumerable<KeyValuePair<string, T>> GetEnumList<T>()
   2:        {
   3:            var x = typeof(T).GetFields().Where(info => info.FieldType.Equals(typeof(T)));
   4:            return from field in x
   5:                   select new KeyValuePair<string, T>(GetEnumDescription(field), (T)Enum.Parse(typeof(T), field.Name, false)); 
   6:        }
   8: private static string GetEnumDescription(FieldInfo field)
   9:        {
  10:            DescriptionAttribute[] attributes = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
  11:            if (attributes.Length > 0)
  12:            {
  13:                return attributes[0].Description;
  14:            }
  15:            else
  16:            {
  17:                return field.Name;
  18:            }
  19:        }

Finally, we will have something like this


That’s it. Let me know if you have any comments.