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

Boris picture Boris · Nov 29, 2010 · Viewed 41.6k times · Source

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.

Answer

Fredrik Hedblad picture Fredrik Hedblad · Nov 29, 2010

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