Tuesday 6 February 2007

Using a ComboBox to select an Enum value in XAML

It is often a requirement in UI to provide a combo box which is used to select an Enum value. There are many ways to achieve this task. However, some are more elegant than others. Here I present a simple way to do this which uses nothing but XAML.

Firstly, we wish to get all the possible values of the Enum and set them to the ItemsSource of the ComboBox. For this, we can utilise the static GetValues() method on the Enum class. This can be done in XAML using an ObjectDataProvider. We then bind the ItemsSource property of our ComboBox to the ObjectDataProvider.

For this example, I am displaying the "BindingMode" enumeration which is built into WPF.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <Page.Resources>
        <ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="PossibleValues">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="BindingMode" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Page.Resources>
    <ComboBox ItemsSource="{Binding Source={StaticResource PossibleValues}}" SelectedValue="{Binding Source={StaticResource PossibleValues}, Path=[4]}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Page>

Note that in order to use the GetValues() method, we need to include a reference to the System namespace in the mscorlib assembly.

So now we have a ComboBox which looks something like this:

Well that's all good, but we can do better than that! In most applications we want to provide descriptive text for the values than just the ToString() of the Enum.

As usual, DataTemplates come to our rescue - we can do it as follows:

<DataTemplate DataType="{x:Type BindingMode}">
    <TextBlock Text="{Binding}" x:Name="PART_Text" />
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding}" Value="OneWay">
            <Setter TargetName="PART_Text" Property="Text" Value="One way binding" />
            <Setter TargetName="PART_Text" Property="FontWeight" Value="Bold" />
        </DataTrigger>
        <DataTrigger Binding="{Binding}" Value="TwoWay">
            <Setter TargetName="PART_Text" Property="Text" Value="Two Way Binding" />
        </DataTrigger>
        <DataTrigger Binding="{Binding}" Value="OneTime">
            <Setter TargetName="PART_Text" Property="Visibility" Value="Collapsed" />
        </DataTrigger>
        <DataTrigger Binding="{Binding}" Value="OneWayToSource">
            <Setter TargetName="PART_Text" Property="Text" Value="One Way To Source Binding" />
        </DataTrigger>
        <DataTrigger Binding="{Binding}" Value="Default">
            <Setter TargetName="PART_Text" Property="Text" Value="Default Binding" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Here is the final ComboBox:

I could have been more adventurous and used some images or colours or anything really in the DataTemplate, but I hope this example is enough to demonstrate what you can achieve.