Me gusta que todos los objetos que estoy vinculando se definan en mi ViewModel, así que trato de evitar usarlos <ObjectDataProvider>en el xaml cuando sea posible.
Mi solución no utiliza datos definidos en la Vista ni código subyacente. Solo un DataBinding, un ValueConverter reutilizable, un método para obtener una colección de descripciones para cualquier tipo de Enum y una única propiedad en ViewModel para enlazar.
Cuando quiero vincular un Enuma un ComboBoxtexto que quiero mostrar, nunca coincide con los valores de Enum, entonces uso el [Description()]atributo para darle el texto que realmente quiero ver en el ComboBox. Si tuviera una enumeración de días de la semana, se vería así:
public enum DayOfWeek
{
// add an optional blank value for default/no selection
[Description("")]
NOT_SET = 0,
[Description("Sunday")]
SUNDAY,
[Description("Monday")]
MONDAY,
...
}
Primero creé la clase auxiliar con un par de métodos para lidiar con las enumeraciones. Un método obtiene una descripción de un valor específico, el otro método obtiene todos los valores y sus descripciones para un tipo.
public static class EnumHelper
{
public static string Description(this Enum value)
{
var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return (attributes.First() as DescriptionAttribute).Description;
// If no description is found, the least we can do is replace underscores with spaces
// You can add your own custom default formatting logic here
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
}
public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
{
if (!t.IsEnum)
throw new ArgumentException($"{nameof(t)} must be an enum type");
return Enum.GetValues(t).Cast<Enum>().Select((e) => new ValueDescription() { Value = e, Description = e.Description() }).ToList();
}
}
A continuación, creamos un ValueConverter. Heredar de MarkupExtensionhace que sea más fácil de usar en XAML, por lo que no tenemos que declararlo como un recurso.
[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return EnumHelper.GetAllValuesAndDescriptions(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Mi ViewModelsólo tiene 1 propiedad que mi Viewpuede unirse a tanto para el SelectedValuey ItemsSourcedel cuadro combinado:
private DayOfWeek dayOfWeek;
public DayOfWeek SelectedDay
{
get { return dayOfWeek; }
set
{
if (dayOfWeek != value)
{
dayOfWeek = value;
OnPropertyChanged(nameof(SelectedDay));
}
}
}
Y finalmente para enlazar la ComboBoxvista (usando el ValueConverteren el ItemsSourceenlace) ...
<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=SelectedDay}" />
Para implementar esta solución, solo necesita copiar mi EnumHelperclase y mi EnumToCollectionConverterclase. Trabajarán con cualquier enumeración. Además, no lo incluí aquí, pero la ValueDescriptionclase es solo una clase simple con 2 propiedades de objeto público, una llamada Value, una llamada Description. Puede crearlo usted mismo o puede cambiar el código para usar un Tuple<object, object>oKeyValuePair<object, object>