2010-11-29 20 views
20

Ho una classe:Associazione dati WPF: come associare un enum a una casella combinata utilizzando 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; 
    } 
} 

e un enum:

public enum DetailScope 
{ 
    Private, 
    Business, 
    OtherDetail 
} 

Infine, avere un file 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> 

Vorrei fare due cose:

  1. Desidero eseguire il binding dei dati DetailsScope valori enum ai valori della casella combinata. Non desidero confermare direttamente i valori enum perché l'ultimo valore enum sarebbe OtherDetail anziché Other detail (aggiunto un carattere di spazio e una piccola lettera 'd').
  2. Desidero associare il valore selezionato nella casella combinata a quello specificato nell'istanza dell'oggetto AccountDetail.

Potrebbe aiutarmi? Grazie.

Aggiornamento: Ho trovato questo post http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx. Ho bisogno di qualcosa di simile.

risposta

37

un modo abbastanza semplice per farlo è quello di utilizzare un ObjectDataProvider

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

Utilizzare l'ObjectDataProvider come ItemsSource per ComboBox, si legano SelectedItem alla proprietà Campo di applicazione e applicare un convertitore per il display di ogni 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> 

E nel convertitore è possibile utilizzare lo splitter per corde Regex per CamelCase disponibile nella domanda this. Ho usato la versione più avanzata ma probabilmente puoi usarne una più semplice. OtherDetail + the regex = Altri dettagli. Rendere il valore di ritorno più basso e quindi restituire una stringa con il primo carattere UpperCase dovrebbe darti il ​​risultato previsto

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

È fantastico! Grazie, Meleak – Boris

2

Ecco una soluzione: si crea una proprietà (un elenco) che contiene tutte le possibilità e si associa il ComboBox su quella proprietà.

In XAML:

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

E nel codice dietro:

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, grazie per la risposta. Sto cercando una soluzione che è più orientata XAML, qualcosa del tipo: http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx – Boris

11

Il modo in cui ho sempre fatto, è il seguente. La bellezza di questa soluzione è che è completamente generica e può essere riutilizzata per qualsiasi tipo di enumerazione.

1) Quando si definisce un'enumerazione, utilizzare alcuni attributi personalizzati per fornire alcune informazioni. In questo esempio ho usato Browsable (false) per indicare che questo enumeratore è interno e non voglio vedere questa opzione in una casella combinata. Descrizione ("") mi consente di specificare un nome visualizzato per l'enumerazione.

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

2) Definire una classe che ho chiamato EnumerationManager. Questa è una classe generica che analizza un tipo di Enumerazione e genera un elenco di valori. Se un enumeratore ha Browsable impostato su false, verrà saltato. Se ha un attributo Description, utilizzerà la stringa descrittiva come nome visualizzato. Se non viene trovata alcuna descrizione, mostrerà solo la stringa predefinita dell'enumeratore.

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) Nel vostro XAML aggiungere una sezione nel vostro ResourceDictionary

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

4) Ora basta legare l'ItemsSource della vostra casella combinata a questa chiave che abbiamo appena definito nel dizionario risorse

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

Se provi questo codice usando la mia enumerazione sopra, dovresti vedere 3 articoli nella tua casella combinata:

Item 1 
Item 2 
Item3 

Spero che questo aiuti.

MODIFICHE: Se si aggiunge l'implementazione di LocalizableDescriptionAttribute e si utilizza l'attributo Descrizione che stavo usando sarebbe perfetto.

+0

Liz, grazie per la tua risposta. – Boris

+0

Questo è un metodo eccellente. – gakera

+0

Qual è il modo migliore per associare nuovamente l'oggetto selezionato a viewmodel? Sto provando a collegarmi direttamente a un enum dello stesso tipo nel viewmodel, ma poi ottiene la descrizione come una stringa inviata dal controllo e analizzandola (specialmente se localizzata) è un dolore? Convertitore – gakera

0

Vorrei usare un convertitore di valori per questo, questo ti permetterà di legare direttamente usando il convertitore, puoi cambiare l'implementazione di Converti per creare una presentazione "più bella" leggibile dall'uomo delle enumerazioni, cioè divisa in caratteri maiuscoli.

C'è un articolo completo su questo approccio 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

è una grande idea. come faccio a legare? – Boris

+0

@ Boris: è tutto dettagliato nell'articolo collegato sopra – BrokenGlass

Problemi correlati