¿Cómo vincular una enumeración a un control de cuadro combinado en WPF?

Resuelto Joan Venge asked hace 13 años • 22 respuestas

Estoy tratando de encontrar un ejemplo simple donde las enumeraciones se muestren tal como están. Todos los ejemplos que he visto intentan agregar cadenas de visualización atractivas, pero no quiero esa complejidad.

Básicamente, tengo una clase que contiene todas las propiedades que vinculo, estableciendo primero el DataContext en esta clase y luego especificando el enlace de esta manera en el archivo xaml:

<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>

Pero esto no muestra los valores de enumeración en los ComboBoxelementos as.

Joan Venge avatar May 27 '11 05:05 Joan Venge
Aceptado

Puedes hacerlo desde el código colocando el siguiente código en el Loadedcontrolador de eventos de Windows, por ejemplo:

yourComboBox.ItemsSource = Enum.GetValues(typeof(EffectStyle)).Cast<EffectStyle>();

Si necesita vincularlo en XAML, debe usarlo ObjectDataProviderpara crear un objeto disponible como fuente de enlace:

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:System="clr-namespace:System;assembly=mscorlib"
        xmlns:StyleAlias="clr-namespace:Motion.VideoEffects">
    <Window.Resources>
        <ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
                            ObjectType="{x:Type System:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="StyleAlias:EffectStyle"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>
    <Grid>
        <ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}"
                  SelectedItem="{Binding Path=CurrentEffectStyle}" />
    </Grid>
</Window>

Llama la atención sobre el siguiente código:

xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects"

Puede leer una guía sobre cómo asignar espacios de nombres y ensamblados en MSDN .

kyrylomyr avatar May 26 '2011 22:05 kyrylomyr

Me gusta que todos los objetos que estoy vinculando se definan en my ViewModel, por lo que trato de evitar usarlos <ObjectDataProvider>en 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 a la que vincularse.

Cuando quiero vincular un Enuma a ComboBox, el texto que quiero mostrar nunca coincide con los valores de Enum, así que uso el [Description()]atributo (from System.ComponentModel) para darle el texto que realmente quiero ver en 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é una clase auxiliar con un par de métodos para manejar enumeraciones. Un método obtiene una descripción de un valor específico, el otro método obtiene todos los valores y sus descripciones de 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;
  }
}

Solo necesito ViewModel1 propiedad a la que Viewpueda vincularme tanto para el cuadro combinado SelectedValuecomo ItemsSourcepara el cuadro combinado:

private DayOfWeek dayOfWeek;

public DayOfWeek SelectedDay
{
  get { return dayOfWeek; }
  set
  {
    if (dayOfWeek != value)
    {
      dayOfWeek = value;
      OnPropertyChanged(nameof(SelectedDay));
    }
  }
}

Y finalmente para vincular 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, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

Para implementar esta solución solo necesitas copiar mi EnumHelperclase y EnumToCollectionConvertermi clase. Trabajarán con cualquier enumeración. Además, no lo incluí aquí, pero la ValueDescriptionclase es simplemente una clase simple con 2 propiedades de objeto público, una llamada Valuey otra llamada Description. Puede crearlo usted mismo o puede cambiar el código para usar un Tuple<object, object>oKeyValuePair<object, object>


Para los que querían ver la ValueDescriptionclase:

public class ValueDescription
{
    public object Value { get; set; }
    public object Description { get; set; }
}
Nick avatar Sep 14 '2012 18:09 Nick

Utilicé otra solución usando MarkupExtension.

  1. Hice una clase que proporciona la fuente de los elementos:

    public class EnumToItemsSource : MarkupExtension
    {
        private readonly Type _type;
    
        public EnumToItemsSource(Type type)
        {
            _type = type;
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return Enum.GetValues(_type)
                .Cast<object>()
                .Select(e => new { Value = (int)e, DisplayName = e.ToString() });
        }
    }
    
  2. Eso es casi todo... Ahora úsalo en XAML:

        <ComboBox DisplayMemberPath="DisplayName"
              ItemsSource="{persons:EnumToItemsSource {x:Type enums:States}}"
              SelectedValue="{Binding Path=WhereEverYouWant}"
              SelectedValuePath="Value" />
    
  3. Cambie 'enums:States' a su enumeración

tom.maruska avatar Jul 01 '2013 13:07 tom.maruska

Utilice el proveedor de datos de objeto:

<ObjectDataProvider x:Key="enumValues"
   MethodName="GetValues" ObjectType="{x:Type System:Enum}">
      <ObjectDataProvider.MethodParameters>
           <x:Type TypeName="local:ExampleEnum"/>
      </ObjectDataProvider.MethodParameters>
 </ObjectDataProvider>

y luego vincularse al recurso estático:

ItemsSource="{Binding Source={StaticResource enumValues}}"

basado en este artículo

druss avatar Jan 27 '2015 14:01 druss