2010-11-29 14 views
20

tengo una clase:WPF Enlace de datos: ¿cómo se vinculan los datos a una enumeración a un cuadro combinado usando XAML?

public class AccountDetail 
{ 
    public DetailScope Scope 
    { 
     get { return scope; } 
     set { scope = value; } 
    } 

    public string Value 
    { 
     get { return this.value; } 
     set { this.value = value; } 
    } 

    private DetailScope scope; 
    private string value; 

    public AccountDetail(DetailScope scope, string value) 
    { 
     this.scope = scope; 
     this.value = value; 
    } 
} 

y una enumeración:

public enum DetailScope 
{ 
    Private, 
    Business, 
    OtherDetail 
} 

Por último, tengo un archivo .xaml:

<Window x:Class="Gui.Wpf.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Test" 
    SizeToContent="WidthAndHeight"> 

    <Grid> 
     <ComboBox 
      Name="ScopeComboBox" 
      Width="120" 
      Height="23" 
      Margin="12" /> 
    </Grid> 
</Window> 

me gustaría hacer dos cosas:

  1. Deseo que los datos se unan DetailsScope enum valores a los valores del cuadro combinado. No deseo enlazar valores de enum directamente porque el último valor enum sería OtherDetail en lugar de Other detail (se agregó un carácter de espacio y una letra minúscula 'd').
  2. Deseo que los datos enlacen el valor seleccionado en el cuadro combinado al especificado en la instancia del objeto AccountDetail.

¿Podría ayudarme? Gracias.

Actualización: He encontrado este post http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx. Necesito algo similar.

Respuesta

37

Una manera muy fácil de hacer esto es usar un ObjectDataProvider

<ObjectDataProvider MethodName="GetValues" 
        ObjectType="{x:Type sys:Enum}" 
        x:Key="DetailScopeDataProvider"> 
    <ObjectDataProvider.MethodParameters> 
     <x:Type TypeName="local:DetailScope" /> 
    </ObjectDataProvider.MethodParameters> 
</ObjectDataProvider> 

Uso del ObjectDataProvider como el ItemsSource para el cuadro combinado, se unen SelectedItem a la propiedad Alcance y aplicar un convertidor para la visualización de cada ComboBoxItem

<ComboBox Name="ScopeComboBox" 
      ItemsSource="{Binding Source={StaticResource DetailScopeDataProvider}}" 
      SelectedItem="{Binding Scope}" 
      Width="120" 
      Height="23" 
      Margin="12"> 
    <ComboBox.ItemTemplate> 
     <DataTemplate> 
      <TextBlock Text="{Binding Converter={StaticResource CamelCaseConverter}}"/> 
     </DataTemplate> 
    </ComboBox.ItemTemplate> 
</ComboBox> 

Y en el convertidor puede usar Regex para CamelCase string splitter encontrado en this pregunta. Usé la versión más avanzada, pero probablemente puedas usar una más simple. OtherDetail + la expresión regular = Otro detalle. Hacer que el valor de retorno baje y luego devolver una cadena con el primer carácter. UpperCase debería darle el resultado esperado

public class CamelCaseConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     string enumString = value.ToString(); 
     string camelCaseString = Regex.Replace(enumString, "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 ").ToLower(); 
     return char.ToUpper(camelCaseString[0]) + camelCaseString.Substring(1); 
    } 
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return value; 
    } 
} 
+0

¡Eso es increíble! Gracias, Meleak – Boris

2

Aquí hay una solución: crea una propiedad (una lista) que contiene todas las posibilidades y enlaza su ComboBox en esa propiedad.

En el XAML:

<ComboBox 
    Name="ScopeComboBox" 
    Width="120" 
    Height="23" 
    Margin="12" 
    ItemsSource="{Binding Path=AccountDetailsProperty}" 
    DisplayMemberPath="Value"/> 

Y en el código subyacente:

public partial class Window1 : Window 
{ 
    public Window1() 
    { 
     AccountDetailsProperty = new List<AccountDetail>() 
     { 
      new AccountDetail(DetailScope.Business, "Business"), 
      new AccountDetail(DetailScope.OtherDetail, "Other details"), 
      new AccountDetail(DetailScope.Private, "Private"), 
     }; 

     InitializeComponent(); 
     this.DataContext = this; 
    } 

    public List<AccountDetail> AccountDetailsProperty { get; set; } 
} 
+0

Nicolas, gracias por la respuesta. Estoy buscando una solución que esté más orientada a XAML, algo así como: http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx – Boris

11

La forma en que siempre he hecho es como sigue. La belleza de esta solución es que es completamente genérica y puede reutilizarse para cualquier tipo de enumeración.

1) Cuando defina una enumeración, utilice algunos atributos personalizados para proporcionar un poco de información. En este ejemplo, he usado Browsable (falso) para indicar que este enumerador es interno, y no quiero ver esta opción en un cuadro combinado. Descripción ("") me permite especificar un nombre para mostrar para la enumeración.

public enum MyEnumerationTypeEnum 
    { 
    [Browsable(false)] 
    Undefined, 
    [Description("Item 1")] 
    Item1, 
    [Description("Item 2")] 
    Item2, 
    Item3 
    } 

2) Define una clase que he llamado EnumerationManager. Esta es una clase genérica que analiza un tipo de enumeración y genera una lista de valores. Si un enumerador tiene Browsable establecido en falso, se saltará. Si tiene un atributo Descripción, usará la cadena de descripción como nombre para mostrar. Si no se encuentra una descripción, solo mostrará la cadena predeterminada del enumerador.

public class EnumerationManager 
    { 
    public static Array GetValues(Type enumeration) 
    { 
     Array wArray = Enum.GetValues(enumeration); 
     ArrayList wFinalArray = new ArrayList(); 
     foreach(Enum wValue in wArray) 
     { 
     FieldInfo fi = enumeration.GetField(wValue.ToString()); 
     if(null != fi) 
     { 
      BrowsableAttribute[] wBrowsableAttributes = fi.GetCustomAttributes(typeof(BrowsableAttribute),true) as BrowsableAttribute[]; 
      if(wBrowsableAttributes.Length > 0) 
      { 
      // If the Browsable attribute is false 
      if(wBrowsableAttributes[0].Browsable == false) 
      { 
       // Do not add the enumeration to the list. 
       continue; 
      }   
      } 

      DescriptionAttribute[] wDescriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute),true) as DescriptionAttribute[]; 
     if(wDescriptions.Length > 0) 
     { 
     wFinalArray.Add(wDescriptions[0].Description); 
     } 
     else 
     wFinalArray.Add(wValue); 
     } 
     } 

     return wFinalArray.ToArray(); 
    } 
    } 

3) En su xaml añadir una sección en su ResourceDictionary

<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type l:EnumerationManager}" x:Key="OutputListForMyComboBox"> 
     <ObjectDataProvider.MethodParameters> 
      <x:Type TypeName="l:MyEnumerationTypeEnum" /> 
     </ObjectDataProvider.MethodParameters> 
    </ObjectDataProvider> 

4) Ahora acaba de obligar a la ItemsSource de su cuadro combinado a esta clave que acabamos de definir en el diccionario de recursos

<ComboBox Name="comboBox2" 
      ItemsSource="{Binding Source={StaticResource OutputListForMyComboBox}}" /> 

Si prueba este código utilizando mi enumeración anterior, debería ver 3 elementos en su cuadro combinado:

Item 1 
Item 2 
Item3 

Espero que esto ayude.

EDICIONES: Si agrega la implementación de LocalizableDescriptionAttribute y utiliza en su lugar el atributo Description que estaba usando, sería perfecto.

+0

Liz, gracias por tu respuesta. – Boris

+0

Este es un excelente método. – gakera

+0

¿Cuál es la mejor manera de vincular SelectedItem de nuevo al viewmodel? Intento unirme directamente a una enumeración del mismo tipo en el modelo de vista, pero luego obtiene la descripción como una cadena enviada desde el control y analizarla (especialmente si está localizado) es un problema. – gakera

0

Yo usaría un convertidor de valor para esto, esto le permitirá enlazar directamente usando el convertidor, puede cambiar la implementación Convertir para hacer una presentación "más agradable" legible por humanos de las enumeraciones, es decir, dividir en caracteres en mayúscula.

Hay un artículo completo sobre este enfoque here.

public class MyEnumToStringConverter : IValueConverter 
    { 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return value.ToString(); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return (MyEnum) Enum.Parse(typeof (MyEnum), value.ToString(), true); 
    } 
    } 

+0

convertidor es una gran idea. ¿cómo hago el enlace? – Boris

+0

@Boris: todo está detallado en el artículo vinculado anteriormente – BrokenGlass

Cuestiones relacionadas