So, the web, and StackOverflow, have plenty of nice answers for how to bind a combobox to an enum property in WPF. But Silverlight is missing all of the features that make this possible :(. For example:
EnumDisplayer
-style IValueConverter
that accepts a type parameter, since Silverlight doesn't support x:Type
.ObjectDataProvider
, like in this approach, since it doesn't exist in Silverlight.Type
properties of the object, since generics aren't supported in XAML (and the hacks to make them work all depend on markup extensions, not supported in Silverlight).Massive fail!
As I see it, the only way to make this work is to either
IValueConverter
for every enum I want to bind to.Are there any alternatives that are more generic, i.e. don't involve writing the same code over and over for every enum I want? I suppose I could do solution #2 using a generic class accepting the enum as a type parameter, and then create new classes for every enum I want that are simply
class MyEnumConverter : GenericEnumConverter<MyEnum> {}
What are your thoughts, guys?
Agh, I spoke too soon! There is a perfectly good solution, at least in Silverlight 3. (It might only be in 3, since this thread indicates that a bug related to this stuff was fixed in Silverlight 3.)
Basically, you need a single converter for the ItemsSource
property, but it can be entirely generic without using any of the prohibited methods, as long as you pass it the name of a property whose type is MyEnum
. And databinding to SelectedItem
is entirely painless; no converter needed! Well, at least it is as long as you don't want custom strings for each enum value via e.g. the DescriptionAttribute
, hmm... will probably need another converter for that one; hope I can make it generic.
Update: I made a converter and it works! I have to bind to SelectedIndex
now, sadly, but it's OK. Use these guys:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Data; namespace DomenicDenicola.Wpf { public class EnumToIntConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // Note: as pointed out by Martin in the comments on this answer, this line // depends on the enum values being sequentially ordered from 0 onward, // since combobox indices are done that way. A more general solution would // probably look up where in the GetValues array our value variable // appears, then return that index. return (int)value; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return Enum.Parse(targetType, value.ToString(), true); } } public class EnumToIEnumerableConverter : IValueConverter { private Dictionary<Type, List<object>> cache = new Dictionary<Type, List<object>>(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var type = value.GetType(); if (!this.cache.ContainsKey(type)) { var fields = type.GetFields().Where(field => field.IsLiteral); var values = new List<object>(); foreach (var field in fields) { DescriptionAttribute[] a = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false); if (a != null && a.Length > 0) { values.Add(a[0].Description); } else { values.Add(field.GetValue(value)); } } this.cache[type] = values; } return this.cache[type]; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } }
With this sort of binding XAML:
<ComboBox x:Name="MonsterGroupRole" ItemsSource="{Binding MonsterGroupRole, Mode=OneTime, Converter={StaticResource EnumToIEnumerableConverter}}" SelectedIndex="{Binding MonsterGroupRole, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}" />
And this sort of resource-declaration XAML:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ddwpf="clr-namespace:DomenicDenicola.Wpf"> <Application.Resources> <ddwpf:EnumToIEnumerableConverter x:Key="EnumToIEnumerableConverter" /> <ddwpf:EnumToIntConverter x:Key="EnumToIntConverter" /> </Application.Resources> </Application>
Any comments would be appreciated, as I'm somewhat of a XAML/Silverlight/WPF/etc. newbie. For example, will the EnumToIntConverter.ConvertBack
be slow, so that I should consider using a cache?
There is another way to bind ComboBox to enums without the need of a custom converter for the selected item. You can check it at
http://charlass.wordpress.com/2009/07/29/binding-enums-to-a-combobbox-in-silverlight/
It doesn't use the DescriptionAttributes.... but it works perfectly for me, so i guess it depends on the scenario it will be used
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With