Por tanto, la web, y StackOverflow, tienen un montón de buenos respuestas a cómo enlazar un combobox a una enumeración de propiedad en WPF. Pero Silverlight es que faltan todas las características que hacen que esto sea posible :(. Por ejemplo:

  1. Usted no puede usar un genérico EnumDisplayerestilo IValueConverter que acepta un parámetro de tipo, ya que Silverlight no admite x:Type.
  2. No puede usar ObjectDataProvider, como en este enfoque, ya que no existe en Silverlight.
  3. Usted no puede utilizar una costumbre de la extensión de marcado como en los comentarios en el enlace de #2, desde extensiones de marcado no existe en Silverlight.
  4. Usted no puede hacer una versión de #1 el uso de medicamentos genéricos en lugar de Type propiedades del objeto, puesto que los medicamentos genéricos no son compatibles en XAML (y los hacks para que funcionen dependen de extensiones de marcado, no se admiten en Silverlight).

Masiva fallar!

Como yo lo veo, la única manera de hacer este trabajo es

  1. Trampa y se unen a una cadena de propiedad en mi ViewModel, cuya setter/getter hace la conversión, valores de carga en el ComboBox el uso de código subyacente en la Vista.
  2. Hacer una personalizada IValueConverter para cada enum quiero enlazar.

Hay alternativas que son más genéricos, es decir, no implican escribir el mismo código una y otra vez para cada enum quiero? Supongo que me podría hacer la solución #2 uso de una clase genérica de aceptar el enum como un parámetro de tipo y, a continuación, crear nuevas clases para cada enum quiero que simplemente

class MyEnumConverter : GenericEnumConverter<MyEnum> {}

¿Cuáles son sus pensamientos, chicos?

InformationsquelleAutor Domenic | 2009-08-15

4 Comentarios

  1. 34

    Agh, me habló demasiado pronto! Hay una perfectamente buena solución, al menos en Silverlight 3. (Es posible que solo sea en 3, ya que este hilo indica que un error relacionado con este material fue fijado en Silverlight 3.)

    Básicamente, usted necesita un solo convertidor para el ItemsSource de la propiedad, pero puede ser totalmente genérica sin necesidad de utilizar cualquiera de los métodos prohibidos, siempre y cuando se le pasa el nombre de una propiedad cuyo tipo es MyEnum. Y el enlace de datos para SelectedItem es totalmente indoloro, no converter es necesario! Bueno, al menos es igual de largo que usted no desea cadenas personalizadas para cada valor de enumeración a través de por ejemplo, la DescriptionAttribute, hmm… probablemente necesitará otro convertidor para que uno; espero que puedo hacer es genérico.

    Actualización: hice un convertidor y funciona! Tengo que enlazar a SelectedIndex ahora, tristemente, pero está bien. El uso de estos chicos:

    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 este tipo de unión XAML:

    <ComboBox x:Name="MonsterGroupRole"
    ItemsSource="{Binding MonsterGroupRole,
    Mode=OneTime,
    Converter={StaticResource EnumToIEnumerableConverter}}"
    SelectedIndex="{Binding MonsterGroupRole,
    Mode=TwoWay,
    Converter={StaticResource EnumToIntConverter}}" />

    Y este tipo de recurso-declaración de 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>

    Cualquier comentario se agradece, como soy un poco de un XAML/Silverlight/WPF/etc. novato. Por ejemplo, el EnumToIntConverter.ConvertBack ser lento, por lo que debería considerar el uso de una caché?

    • Definitivamente caché de todas las cosas que estamos haciendo con el Tipo de objeto (es decir, GetFields()) como es la reflexión y en general se considera a ser lento (aunque por supuesto depende de la aplicación del uso de la reflexión). Aparte de eso buen trabajo!
    • muy útil. gracias. ¿alguna vez dar la vuelta a la extensión de este para la traducción fácil de valores, como OrderStatus.NewOrder a «Nuevo Orden» ?
    • En efecto, el código anterior se analizar cualquier DescriptionAttributes agregar a la enumeración de los campos :).
    • Si los valores de la enumeración no están numerados secuencialmente desde 0 esta solución va a fallar. Depende de la correspondencia uno a uno entre los valores enum y los índices en el combobox. De lo contrario, una buena solución.
    • Gracias por decírmelo, Martin! Parece que se pueden corregir, aunque, si he de hacer una búsqueda en el índice que el valor que aparece en el interior de GetValues o lo que sea. Voy a actualizar la respuesta.
    • En EnumToIntConverter.ConvertBack(), en lugar de devolver el valor a través de la lenta conversión de cadena de & análisis, se podría utilizar return Enum.ToObject (targetType, value); lugar.
    • +1 para la solución de su problema y publicarlo para que otros se beneficien de!

  2. 4

    Hay otra manera de enlazar ComboBox para las enumeraciones sin la necesidad de un convertidor personalizado para el elemento seleccionado. Puede comprobarlo en

    http://charlass.wordpress.com/2009/07/29/binding-enums-to-a-combobbox-in-silverlight/

    No use la DescriptionAttributes…. pero funciona a la perfección para mí, así que supongo que depende del escenario que será utilizado

    • Enlace útil, pero es mejor incluir a partes significativas en la respuesta en caso de que el vínculo se rompe.
  3. 4

    Me parece que un simple proceso de encapsulación de datos de enumeración es la forma más fácil de usar.

    public ReadOnly property MonsterGroupRole as list(of string)
    get
    return [Enum].GetNames(GetType(GroupRoleEnum)).Tolist
    End get
    End Property
    private _monsterEnum as GroupRoleEnum
    Public Property MonsterGroupRoleValue as Integer
    get
    return _monsterEnum
    End get
    set(value as integer)
    _monsterEnum=value
    End set
    End Property

    <ComboBox x:Name="MonsterGroupRole"
    ItemsSource="{Binding MonsterGroupRole,
    Mode=OneTime}"
    SelectedIndex="{Binding MonsterGroupRoleValue ,
    Mode=TwoWay}" />

    Y esto va a eliminar completamente la necesidad de un convertidor de… 🙂

    • lol esto es sólo la creación manual de un convertidor de que sólo funciona con un enum.
  4. 0

    Aquí es el mismo de la instalación de un Windows 8.1/Windows Phone App universal, los principales cambios son:-

    • Falta DescriptionAttribute en el marco (o al menos yo no lo encuentro)
    • Diferencias en cómo la reflexión obras (utilizando TypeInfo.Declarado campos)

    Parece que el orden de los XAML es importante también, tuve que poner ItemsSource antes de SelectedIndex de lo contrario, no llame a la ItemsSource de unión
    por ejemplo,

    <ComboBox
    ItemsSource="{Binding Path=MyProperty,Mode=OneWay, Converter={StaticResource EnumToIEnumerableConverter}}"
    SelectedIndex="{Binding Path=MyProperty, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}" 
    />

    Código de abajo

    namespace MyApp.Converters
    {
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Windows.UI.Xaml.Data;
    public class EnumToIntConverter : IValueConverter
    {
    public object Convert(object value, Type targetType, object parameter, string language)
    {
    //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, string language)
    {
    return value;
    }
    }
    public class EnumToIEnumerableConverter : IValueConverter
    {
    private readonly Dictionary<TypeInfo, List<object>> _cache = new Dictionary<TypeInfo, List<object>>();
    public object Convert(object value, Type targetType, object parameter, string language)
    {
    var type = value.GetType().GetTypeInfo();
    if (!_cache.ContainsKey(type))
    {
    var fields = type.DeclaredFields.Where(field => field.IsLiteral);
    var values = new List<object>();
    foreach (var field in fields)
    {
    var a = (DescriptionAttribute[]) field.GetCustomAttributes(typeof(DescriptionAttribute), false);
    if (a != null && a.Length > 0)
    {
    values.Add(a[0].Description);
    }
    else
    {
    values.Add(field.GetValue(value));
    }
    }
    _cache[type] = values;
    }
    return _cache[type];
    }
    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
    throw new NotImplementedException();
    }
    }
    [AttributeUsage(AttributeTargets.Field)]
    public class DescriptionAttribute : Attribute
    {
    public string Description { get; private set; }
    public DescriptionAttribute(string description)
    {
    Description = description;
    }
    }
    }

Dejar respuesta

Please enter your comment!
Please enter your name here