Agh, ho parlato troppo presto! C'è a perfectly good solution, almeno in Silverlight 3. (Potrebbe essere solo in 3, in quanto this thread indica che un bug relativo a questa roba è stato fissato in Silverlight 3.)
Fondamentalmente, è necessario un unico convertitore per la proprietà ItemsSource
, ma può essere completamente generico senza utilizzare nessuno dei metodi proibiti, a condizione che tu lo passi il nome di una proprietà il cui tipo sia MyEnum
. E l'associazione di dati a SelectedItem
è completamente indolore; nessun convertitore necessario! Bene, almeno è finchè non vuoi stringhe personalizzate per ogni valore enum, ad es. il DescriptionAttribute
, hmm ... probabilmente avrà bisogno di un altro convertitore per quello; spero di poterlo fare generico.
Aggiornamento: Ho realizzato un convertitore e funziona! Devo legare a SelectedIndex
ora, purtroppo, ma va bene.Utilizzare questi ragazzi:
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();
}
}
}
Con questo tipo di XAML vincolante:
<ComboBox x:Name="MonsterGroupRole"
ItemsSource="{Binding MonsterGroupRole,
Mode=OneTime,
Converter={StaticResource EnumToIEnumerableConverter}}"
SelectedIndex="{Binding MonsterGroupRole,
Mode=TwoWay,
Converter={StaticResource EnumToIntConverter}}" />
E questo tipo di risorsa dichiarazione 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>
Qualsiasi commento sarebbe apprezzato, come ho' m un po 'di XAML/Silverlight/WPF/etc. novizio. Ad esempio, lo EnumToIntConverter.ConvertBack
sarà lento, quindi dovrei considerare l'utilizzo di una cache?
Sicuramente cache tutte le cose che si sta facendo con l'oggetto tipo (ad esempio GetFields()) in quanto è la riflessione e generalmente considerato lento (anche se ovviamente dipende da l'uso della riflessione della tua applicazione). A parte quel bel lavoro! –
molto utile. Grazie. hai mai provato ad estendere questo per una facile traduzione dei valori - come OrderStatus.NewOrder su "New Order"? –
Infatti, il codice sopra analizzerà qualsiasi 'DescriptionAttributes' che aggiungi ai campi enum :). – Domenic