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.

5 comments:

Anonymous said...

Dear,
I need some posts of angelos.
it was a series of five articles on his blog.
POOR MSDN ARTICLE ON BUSINESS ENTITIES....
Currently his blog links are down.do you have any direct contact to him?
or if you have these articles please do send me anti.Genius@hotmail.com
yours truly,
Muhammad Abubakar Dar

ionymous said...

Setting Visibility on an item in your combobox seems like a good idea, but try using the keyboard up/down arrow on the combobox.
"OneTime" is still selectable.

Any suggestions on how to fix this?

Neil Mosafi said...

Not sure, have you tried the IsTabStop property?

Conor said...

Thanks for the post, it really helped me solve my problem of binding a combo box to an enum.

You should explain more clearly that the DataTemplate must be contained within the ItemTemplate of the ComboBox. You cannot use ItemsSource and DataTemplate on the ComboBox itself because that throws an error.

The complete solution is:

Conor said...

Well, it won't accept straight XML in the comments, but here is the general idea:

< ComboBox ItemsSource="{Binding Source={StaticResource PossibleValues}}">< ComboBox.ItemTemplate > < DataTemplate DataType="{x:Type BindingMode}">< TextBlock Text="{Binding}" x:Name="PART_Text" />< DataTemplate.Triggers>< Setter />