We've already seen a number of examples of data binding to enumeration instances. We've seen converters that we can use to convert our enumeration values and Extension Methods that we can use to extract additional information from each member. Earlier in this chapter, we even saw a full but basic example using our BitRate enumeration. Now, with our new found knowledge, let's see how we can improve that earlier example.
As noted, in the previous example, we manually declared a RadioButton control for each of our enumerations. While that is fine for our three member enumeration, it wouldn't make so much sense to use this method if we had a large number of members. Instead, let's think about how we could use a DataTemplate to declare how each member should be rendered. Let's remind ourselves how we declared each RadioButton in the previous example:
<RadioButton Content="16 bits" IsChecked="{Binding BitRate, Converter={StaticResource EnumToBoolConverter}, ConverterParameter=Sixteen}" VerticalContentAlignment="Center" />
The first thing that we notice is the hardcoded Content value. Obviously, we can't do this in a DataTemplate, otherwise every member would be given the same label. This is a perfect place for us to use the EnumToDescriptionStringConverter converter that we created earlier, so let's update that now:
<UserControl.Resources> ... <Converters:EnumToDescriptionStringConverter x:Key="EnumToDescriptionStringConverter" /> ... </UserControl.Resources> ... <RadioButton Content="{Binding ., Converter={StaticResource EnumToDescriptionStringConverter}}" IsChecked="{Binding BitRate, Converter={StaticResource EnumToBoolConverter}, ConverterParameter=Sixteen}" VerticalContentAlignment="Center" />
Next, we see that we have also hardcoded the Sixteen enumeration member to the ConverterParameter property, so we'll need to change that in our data template too. Our first attempt might be to simply data bind the whole data context from the data template, which in our case, is one of the enumeration instances:
<RadioButton Content="{Binding ., Converter={StaticResource EnumToDescriptionStringConverter}}" IsChecked="{Binding BitRate, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={Binding}}" VerticalContentAlignment="Center" />
However, if we do this and run the application, we will receive the following exception:
A 'Binding' cannot be set on the 'ConverterParameter' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
Unfortunately, we cannot data bind to the ConverterParameter property, as it was not declared as a Dependency Property. As we cannot data bind to this property from within our data template and no longer use the EnumToBoolConverter class to specify the selected enumeration instance, this will complicate our example somewhat.
One trick that we can use is to utilize the SelectedItem property of the ListBoxItem class to hold the value of our selected enumeration member instead. We can achieve this by data binding this property to the IsChecked property of each RadioButton using a RelativeSource.FindAncestor binding in our DataTemplate:
<RadioButton Content="{Binding ., Converter={StaticResource EnumToDescriptionStringConverter}}" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, FallbackValue=False}" VerticalContentAlignment="Center" />
Note that each data item in a collection control will be implicitly wrapped in a UI container element. In our case, we'll use a ListBox control and so our enumeration instances will be wrapped in ListBoxItem elements, but if we had chosen a ComboBox for example, then our items' containers would be ComboBoxItem elements. We'll find out more about this in the next chapter, but for now, let's continue looking at this example.
So, now we have data bound the Content property of the RadioButton to the description of each member from the DescriptionAttribute attribute declared in the enumeration and the IsChecked property to the IsSelected property of the ListBoxItem element. However, we have lost the connection to our selected enumeration property from the View Model.
In order to restore this connection, we can data bind the BitRate property to the SelectedItem property of the ListBox control. The WPF Framework implicitly connects this property with the IsSelected property of each ListBoxItem element and so our connection between the BitRate property and the IsChecked property of each button is now restored. Let's see the updated XAML:
<UserControl x:Class="CompanyName.ApplicationName.Views.BitRateView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Converters="clr-namespace:CompanyName.ApplicationName.Converters; assembly=CompanyName.ApplicationName.Converters" xmlns:Enums="clr-namespace:CompanyName.ApplicationName.DataModels.Enums;
assembly=CompanyName.ApplicationName.DataModels"> <UserControl.Resources> <Converters:EnumToBoolConverter x:Key="EnumToBoolConverter" /> </UserControl.Resources> <GroupBox Header="Audio Quality" FontSize="14" Margin="20" HorizontalAlignment="Left" VerticalAlignment="Top" Padding="5"> <ListBox ItemsSource="{Binding BitRates}" SelectedItem="{Binding BitRate}"> <ListBox.ItemTemplate> <DataTemplate DataType="{x:Type Enums:BitRate}"> <RadioButton Content="{Binding ., Converter={StaticResource EnumToDescriptionStringConverter}}" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, FallbackValue=False}" VerticalContentAlignment="Center" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </GroupBox> </UserControl>
To update our earlier example, we need to add the new Enums XAML namespace prefix, so that we can specify our BitRate enumeration type in the data template. Next, we need to update the content of our GroupBox element. Now we're using a ListBox control so that we can take advantage of its item selection capabilities.
We data bind our BitRates collection to the ItemsSource property and our selected BitRate property to the SelectedItem property of the ListBox. The one problem with this method is that as we're now using a ListBox element in our example, we can see it and its contained ListBoxItem objects. This is not how radio buttons are typically displayed:

It's not a terrible problem and it can be easily fixed by declaring a few styles. We'll return to this example in the following chapter and demonstrate how we can style the ListBox element and its items to completely hide their use from the end users.