DevExpress MVVM Framework. EnumItemsSourceBehavior.

27 January 2015

All the features described in this post are available in both the free and non-free versions of the DevExpress MVVM framework (included in the DevExpress WPF component suite).

There are often situations where you need to represent an enumeration as an IEnumerable for use as a data source. A common solution in those cases is to convert the enumeration to an IEnumerable by calling a Enum.GetValues() method in a property of your ViewModel or implementing a custom IValueConverter descendant use in the binding expression for the control’s ItemsSource property.

To avoid implementing more properties for your ViewModel or writing any extra code, we introduced a special behavior: EnumItemsSourceBehavior that generates items based on a specified enumeration type and assigns them to any control supporting the ItemsSource property.

Imagine, you have the following enumeration.

   1: public enum UserRole {
   2:     [Image("pack://application:,,,/Images/Admin.png"), 
   3:      Display(Name = "Admin", Description = "High level of access")]
   4:     Administrator,
   5:     [Image("pack://application:,,,/Images/Moderator.png"), 
   6:      Display(Name = "Moderator", Description = "Average level of access")]
   7:     Moderator,
   8:     [Image("pack://application:,,,/Images/User.png"), 
   9:      Display(Name = "User", Description = "Low level of access")]
  10:     User
  11: }

Where Image is an attribute that allows assigning an image to a corresponding member of the enumeration and Display is a standard Display attribute that provides a general-purpose attribute that lets you specify localizable strings for types and members of entity partial classes.

Alternatively, you can assign these parameters (each enum value’s image and display information for Name and Description) using the DevExpress Fluent API. The DevExpress Fluent API is especially convenient if you aren’t able to modify the exist enum. Below you will find sample code illustrating how to accomplish this task with the Fluent API and MetadataLocator:

   1: private void OnAppStartup_UpdateThemeName(object sender, StartupEventArgs e) {
   2:     DevExpress.Xpf.Core.ApplicationThemeHelper.UpdateApplicationThemeName();
   3:     MetadataLocator.Default = MetadataLocator.Create().AddMetadata<Metadata>();
   4: }
   1: public class Metadata {
   2:     public static void BuildMetadata(EnumMetadataBuilder<UserRole> builder) {
   3:         builder
   4:             .Member(UserRole.Administrator)
   5:                 .DisplayName("Admin")
   6:                 .Description("High level of access")
   7:                 .ImageUri("pack://application:,,,/Images/Admin.png")
   8:             .EndMember()
   9:             .Member(UserRole.Moderator)
  10:                 .DisplayName("Moderator")
  11:                 .Description("Average level of access")
  12:                 .ImageUri("pack://application:,,,/Images/Moderator.png")
  13:             .EndMember()
  14:             .Member(UserRole.User)
  15:                 .DisplayName("User")
  16:                 .Description("Low level of access")
  17:                 .ImageUri("pack://application:,,,/Images/User.png")
  18:             .EndMember();
  19:     }
  20: }

And using the Fluent API and EnumMetadataType attribute:

   1: public class EnumWithDisplayNameMetadata : IEnumMetadataProvider<UserRole> {
   2:     public static void BuildMetadata(EnumMetadataBuilder<UserRole> builder) {
   3:         builder
   4:             .Member(UserRole.Administrator)
   5:                 .DisplayName("Admin")
   6:                 .Description("High level of access")
   7:                 .ImageUri("pack://application:,,,/Images/Admin.png")
   8:             .EndMember()
   9:             .Member(UserRole.Moderator)
  10:                 .DisplayName("Moderator")
  11:                 .Description("Average level of access")
  12:                 .ImageUri("pack://application:,,,/Images/Moderator.png")
  13:             .EndMember()
  14:             .Member(UserRole.User)
  15:                 .DisplayName("User")
  16:                 .Description("Low level of access")
  17:                 .ImageUri("pack://application:,,,/Images/User.png")
  18:             .EndMember();
  19:     }
  20:  
  21:     void IEnumMetadataProvider<UserRole>.BuildMetadata(EnumMetadataBuilder<UserRole> builder) {
  22:         builder
  23:             .Member(UserRole.Administrator)
  24:                 .DisplayName("Admin")
  25:                 .Description("High level of access")
  26:                 .ImageUri("pack://application:,,,/Images/Admin.png")
  27:             .EndMember()
  28:             .Member(UserRole.Moderator)
  29:                 .DisplayName("Moderator")
  30:                 .Description("Average level of access")
  31:                 .ImageUri("pack://application:,,,/Images/Moderator.png")
  32:             .EndMember()
  33:             .Member(UserRole.User)
  34:                 .DisplayName("User")
  35:                 .Description("Low level of access")
  36:                 .ImageUri("pack://application:,,,/Images/User.png")
  37:             .EndMember();
  38:     }
  39: }
   1: [EnumMetadataType(typeof(EnumWithDisplayNameMetadata))]
   2: public enum UserRole {
   3:     Administrator,
   4:     Moderator,
   5:     User
   6: }

To learn more about the DevExpress Fluent API see the DevExpress MVVM Framework. Using DataAnnotation attributes and DevExpress Fluent API post on our blog.

To assign the EnumItemsSourceBehavior to a ComboBoxEdit at design time, perform the following steps:

    1. Open the ComboBoxEdit Smart Tag panel using the Smart Tag glyph SmartTag_Arrow at the top right corner of the control

    1

    2. Open the MVVM tab, click the EnumItemsSourceBehavior item in the «Add Behavior» menu and specify the EnumItemsSourceBehavior’s properties in order, starting from the EnumType property.

    2

    3. Build and run the app to notice the changes to the combobox items at runtime.

    3

    4. You can further customize the editor’s ItemTemplate for the required aesthetic. For example,

    4

The XAML for the above is the following:

   1: <dxe:ComboBoxEdit 
   2:     Name="comboBoxEdit"
   3:     EditValue="{Binding SelectedRole, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"  
   4:     IsTextEditable="False" 
   5:     ApplyItemTemplateToSelectedItem="True" 
   6:     Margin="10">
   7:     <dxmvvm:Interaction.Behaviors>
   8:         <dxmvvm:EnumItemsSourceBehavior EnumType="{x:Type common:UserRole}" SortMode="DisplayName"/>
   9:     </dxmvvm:Interaction.Behaviors>
  10:     <dxe:ComboBoxEdit.ItemTemplate>
  11:         <DataTemplate>
  12:             <Grid>
  13:                 <Grid.RowDefinitions>
  14:                     <RowDefinition/>
  15:                     <RowDefinition/>
  16:                 </Grid.RowDefinitions>
  17:                 <TextBlock Text="{Binding Name}"/>
  18:                 <TextBlock Text="{Binding Description}" Grid.Row="1" FontSize="9"/>
  19:             </Grid>
  20:         </DataTemplate>
  21:     </dxe:ComboBoxEdit.ItemTemplate>
  22: </dxe:ComboBoxEdit>

To represent the enumeration’s element as an item, EnumItemsSourceBehavior uses the EnumMemberInfo class with the following structure:

   1: public class EnumMemberInfo {    
   2:     ...
   3:     public string Name { get; private set; }
   4:     public bool ShowName { get; private set; }
   5:     public object Id { get; private set; }
   6:     public string Description { get; private set; }
   7:     public ImageSource Image { get; private set; }
   8:     public bool ShowImage { get; private set; }
   9:     ...
  10: }

Therefore, the control’s items list and the selected value of the control are of EnumMemberInfo type. For example, the ComboBoxEdit in the above example contains an EnumMemberInfo value in its SelectedItem.

EnumItemsSourceBehavior members

  • ItemTemplate - gets or sets a template that defines the presentation of items contained within the list (this property is defined for any descendants of the standard ItemsControl (ListBox, ListView, etc). If you use DevExpress editors, you’ll need to override the ItemTemplate property)
  • EnumType - gets or sets the type of the specified enumeration
  • UseNumericEnumValue - gets or sets whether an enumeration element can be identified by numeric value
  • SplitNames - gets or sets whether an enumeration element’s name can be split
  • NameConverter - gets or sets a converter used to provide the item’s display value.
  • SortMode - gets or sets how the control’s items will be sorted.

A corresponding example that illustrates how to use the EnumItemsSourceBehavior class in action is available here.

5 comment(s)
Crono

Does the behavior allows filtering some enum values out? I can think of a lot of scenarios where enum types have a default value like "none" or "undefined" that aren't meant to be chosen by a end user.

28 January, 2015
Michael Ch (DevExpress Support)

Hello,

To prevent some enum members from being shown when the EnumItemsSourceBehavior is used, you can forbid creation of corresponding items by setting the DisplayAttribute.AutoGenerateField property to false at the enum member level:

[Image("pack://application:,,,/Images/Admin.png"), Display(Name = "Admin", Description = "High level of access", AutoGenerateField=false)].

Please note that if a control's value is set to a hidden member value, the control will show nothing.

29 January, 2015
CellSmart

Nice.

29 January, 2015
Crono

"Please note that if a control's value is set to a hidden member value, the control will show nothing."

It would be nice for this behavior to support an "UnknownEnumValueTemplate".

29 January, 2015
Michael Ch (DevExpress Support)

There is no a general approach to implement such a property template that will correctly work with all possible associated controls. You can achieve the required functionality by defining a custom ItemTemplate at the associated object level. For example, if you are using the ComboBoxEdit, you can override its DisplayTemplate, EditNonEditableTemplate or ItemTemplate with the required logic. If you have any additional questions or suggestions regarding the EnumItemsSource behavior, please create a corresponding ticket in our Support Center.

30 January, 2015

Please login or register to post comments.