I want to have an Enum on my ViewModel, let's say to represent a person's Gender. The View representing that ViewModel should be able to present a way of supplying that value; whether that is a group of Radio Buttons or a Combo Box (if there are lots). And there are plenty of examples out there where you hard-code Radio Buttons in the XAML each one saying which value it represents. And the better ones will also use the Display Attribute's Name to provide the text for the radio button.
I'm looking to go a step further. I'd like it to generate the RadioButtons dynamically based on the Enum's values and things like the DisplayAttribute's Name and Description. Ideally, I'd like it to choose to create a ComboBox (rather than RadioButtons) if it's more than 6 items (perhaps implemented as a Control of some sort); but let's see if we can walk before we try to run. :)
My googling has got me pretty close... here's what I've got:
public enum Gender
{
[Display(Name="Gentleman", Description = "Slugs and snails and puppy-dogs' tails")]
Male,
[Display(Name = "Lady", Description = "Sugar and spice and all things nice")]
Female
}
Window:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:EnumMultiConverter x:Key="EnumMultiConverter"/>
<ObjectDataProvider
MethodName="GetValues"
ObjectType="{x:Type local:EnumDescriptionProvider}"
x:Key="AdvancedGenderTypeEnum">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:Gender"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding Source={StaticResource AdvancedGenderTypeEnum}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="{Binding GroupName}" Content="{Binding Name}" ToolTip="{Binding Description}">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource EnumMultiConverter}" Mode="TwoWay">
<Binding RelativeSource="{RelativeSource AncestorType=ItemsControl}" Path="DataContext.Gender" Mode="TwoWay" />
<Binding Path="Value" Mode="OneWay"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
EnumDescriptionProvider:
public static class EnumDescriptionProvider
{
public static IList<EnumerationItem> GetValues(Type enumType)
{
string typeName = enumType.Name;
var typeList = new List<EnumerationItem>();
foreach (var value in Enum.GetValues(enumType))
{
FieldInfo fieldInfo = enumType.GetField(value.ToString());
var displayAttribute = (DisplayAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(DisplayAttribute));
if (displayAttribute == null)
{
typeList.Add(new EnumerationItem
{
GroupName = typeName,
Value = value,
Name = value.ToString(),
Description = value.ToString()
});
}
else
{
typeList.Add(new EnumerationItem
{
GroupName = typeName,
Value = value,
Name = displayAttribute.Name,
Description = displayAttribute.Description
});
}
}
return typeList;
}
}
EnumerationItem:
public class EnumerationItem
{
public object GroupName { get; set; }
public object Value { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
And the MultiConverter (because IValueConverter can't take a Binding for the ConverterParameter):
public class EnumMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].Equals(values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
So the only problem I've got is that I can't do the ConvertBack. But maybe someone out there has a brilliant solution. As I say, ideally, I would just want some magical control that I can Bind to the Enum on my ViewModel, and for it to dynamically create RadioButtons for each value for that enum. But I'll take any suggestions that I can get.