Summary
There are many times that there are a group of radio buttons on a form that are associated with a single enumeration. What many developers will do is create a separate property in the ViewModel
for each radio button. I do not like this. First I think that this is too much of a dependency between the View
and the ViewModel
. It also adds a whole bunch of properties to the ViewModel
that just makes the ViewModel
bigger, and it requires excess code for each property to convert it to and from an enumeration. All that is really needed is a single property for each enumeration, and a generic ValueConverter
.
Background
After so many times of having to deal with radio buttons and ViewModel
s, I finally decided to create a generic way of dealing with them.
Implementation
To bind an enumeration to the IsChecked
property of a group of radio buttons, a ValueConverter
will be required; there is just no way to bind an enumeration to a bool
without converting it. It is possible to create a separate ValueConverter
class for each radio button, but that is not required since the ValueConverter
methods have a parameter argument which is set in the XAML by using the ConverterParameter
argument of the Binding. The idea is that each radio button IsChecked
argument is bound to the Enumeration
property using the EnumValueConverter
with a ConverterParameter
equal to a string
which is the name of the specific enumeration.
class EnumValueConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || parameter == null)
return false;
return value.ToString() == parameter.ToString();
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || parameter == null)
return null;
var rtnValue = parameter.ToString();
try
{
object returnEnum = Enum.Parse(targetType, rtnValue);
return returnEnum;
}
catch
{
return null;
}
}
}Colourised in 39ms
In the example code, I have added three debug methods to aid in finding errors, but have removed these methods for clarity. As can be seen, the code is fairly straight forward. In the Convert
method, the ToString()
method is used to convert the enumeration from the ViewModel
to a string
which is then compared to the ConverterParmeter
; if equal, a the value returned is true
. In the ConvertBack
method, the Enum.Parse
method is used to convert the ConverterParmeter
from the control into the appropriate enumeration for the ViewModel
if the value argument in. This fortunately works because in the case of radio buttons, they only need to deal with being changed to the checked state, so never have to deal with changes to the unchecked state; that is automatic. If the Parse does not work, a null
is returned (which is also the case for null
value and parameter argument, which has no effect since null
is invalid for an enumeration (Binding does not allow changes for invalid values).
Wiring up the XAML to the property and converter is quite straightforward. In the example, there is a very simple enum
:
public enum TestEnum
{
A, B, C
}Colourised in 2ms
The property in the ViewModel
for this enumeration is also very straightforward:
public TestEnum EnumBinding
{
get
{
return _enumBinding;
}
set
{
if (_enumBinding != value)
{
_enumBinding = value;
if (PropertyChanged != null)
PropertyChanged(this, new
PropertyChangedEventArgs("EnumBinding"));
}
}
}Colourised in 16ms
Now all that is needed is the XAML, which includes the resource definition for the converter and the checkboxes with the binding definition using the converter and the ConverterParameter
:
<Window x:Class="GenericEnumValueConverter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GenericEnumValueConverter"
Title="Enumerator Value Converter Example"
Height="200" Width="250">
<Window.Resources>
<local:EnumValueConverter x:Key="EnumValueConverter"/>
</Window.Resources>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<RadioButton Content="A"
IsChecked="{Binding EnumBinding,
Converter={StaticResource EnumValueConverter},
ConverterParameter=A}"/>
<RadioButton Content="B"
IsChecked="{Binding EnumBinding,
Converter={StaticResource EnumValueConverter},
ConverterParameter=B}"/>
<RadioButton Content="C"
IsChecked="{Binding EnumBinding,
Converter={StaticResource EnumValueConverter},
ConverterParameter=C}"/>
</StackPanel>
</Window>Colourised in 41ms
The actual example includes a TextBox
to confirm that the property for the enumeration is actually the value of the radio buttons, and an extra radio button containing a bad value to show what happens when there is a bad enumeration value in the XAML (during debug, pressing the “Bad Value” radio button creates a message in the Output window).
Conclusion
This is a very clean way to connect an enumeration to radio buttons. The same converter can also be used for other purposes with a little bit of extra code, such as setting the visibility of controls based on a state that is specified by an enumeration (need to covert the bool
value to a Visibility
enumeration). It removes a lot of complexity from the ViewModel
, and uses a single converter for all instances where enumerations are bound to a CheckBox
(or another Control
). In all, it is a very clean way to reduce code, and improve maintainability.
History
- 15th October, 2011: Initial version