Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Data binding: How to data bind an enum to combo box using XAML? [duplicate]

Tags:

I have a class:

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;     } } 

and an enum:

public enum DetailScope {     Private,      Business,      OtherDetail } 

Lastly, I have a .xaml file:

<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> 

I would like to do two things:

  1. I wish to data bind DetailsScope enum values to the combo box values. I don't wish to bind enum values directly because the last enum value would be OtherDetail instead of Other detail (added a space character and small letter 'd').
  2. I wish to data bind the selected value in the combo box to the one specified in the instance of the AccountDetail object.

Could you help me out? Thanks.

Update: I found this post http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx. I need something similar.

like image 713
Boris Avatar asked Nov 29 '10 18:11

Boris


2 Answers

A pretty easy way to do this is to use an ObjectDataProvider

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

Use the ObjectDataProvider as the ItemsSource for the ComboBox, bind SelectedItem to the Scope property and apply a converter for the display of each 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> 

And in the converter you can use Regex for CamelCase string splitter found in this question. I used the most advanced version but you can probably use a simplier one. OtherDetail + the regex = Other Detail. Making return value lower and then return a string with first Character UpperCase should give you expected result

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;     } } 
like image 58
Fredrik Hedblad Avatar answered Oct 11 '22 14:10

Fredrik Hedblad


The way I have always done it is as follows. The beauty of this solution is that it is completely generic, and can be re-used for any enumeration types.

1) When you define an enumeration make use of some custom attributes to give a little information. In this example I've used Browsable(false) to indicate that this enumerator is internal, and I don't want to see this option in a combo box. Description("") allows me to specify a Display name for the enumeration.

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

2) Define a class which I've called EnumerationManager. This is a generic class that analyzes an Enumeration type and generates a list of values. If an enumerator has Browsable set to false it will be skipped. If it has a Description attribute then it will use the description string as the display name. If no description is found it will just show the default string of the enumerator.

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) In your xaml add a section in your ResourceDictionary

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

4) Now just bind the ItemsSource of your combobox to this key we just defined in the resource dictionary

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

If you try this code using my enumeration above you should see 3 items in your combo box:

Item 1 Item 2 Item3 

Hope this helps.

EDITS: If you add the implementation of the LocalizableDescriptionAttribute and use that instead the Description attribute that I was using that would be perfect.

like image 42
Liz Avatar answered Oct 11 '22 13:10

Liz