WPF数据绑定:如何使用XAML将枚举值绑定到组合框中?

26

我有一个类:

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

还有一个枚举:

public enum DetailScope
{
    Private, 
    Business, 
    OtherDetail
}

最后,我有一个.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>

我想做两件事:

  1. 我希望将DetailsScope枚举值与组合框的值进行数据绑定。我不想直接绑定枚举值,因为最后一个枚举值将是OtherDetail而不是Other detail(添加了空格字符和小写字母“d”)。
  2. 我希望将组合框中选择的值与AccountDetail对象实例中指定的值进行数据绑定。

你能帮我吗?谢谢。

更新:我找到了这篇文章:http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx。我需要类似的东西。

4个回答

44

一个相当简单的方法是使用 ObjectDataProvider。

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

将ObjectDataProvider用作ComboBox的ItemsSource,将SelectedItem绑定到Scope属性,并为每个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>

在转换器中,您可以使用在问题中找到的CamelCase字符串拆分器正则表达式。我使用了最先进的版本,但您可能可以使用更简单的版本。OtherDetail加上正则表达式等于Other Detail。将返回值转换为小写,然后返回一个首字母大写的字符串,就可以得到预期结果。

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

15

我一直使用的方法如下。这种解决方案的美妙之处在于它是完全通用的,可以重用于任何枚举类型。

1)当定义一个枚举时,利用一些自定义属性提供一些信息。在这个例子中,我使用了Browsable(false)来表示枚举器是内部的,我不想在组合框中看到这个选项。Description("")允许我为枚举指定显示名称。

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

2) 定义了一个名为EnumerationManager的类。这是一个通用类,用于分析枚举类型并生成值列表。如果枚举器的Browsable设置为false,则会跳过它。如果它具有Description属性,则将使用描述字符串作为显示名称。如果找不到描述,则只显示枚举器的默认字符串。

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)在您的XAML中,在ResourceDictionary中添加一个部分

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

4) 现在只需要将你的组合框(ComboBox)的ItemsSource绑定到我们在资源字典中定义的这个键即可。

<ComboBox Name="comboBox2" 
          ItemsSource="{Binding Source={StaticResource OutputListForMyComboBox}}" />
如果您使用我上面的枚举尝试此代码,则应在您的组合框中看到3个项目:
Item 1
Item 2
Item3
希望这可以帮到你。
编辑: 如果你添加了 LocalizableDescriptionAttribute 的实现并使用它来替代我使用的 Description 属性,那将是完美的。

这是一个很棒的方法。 - gakera
什么是将SelectedItem绑定回ViewModel的最佳方法?我试图直接绑定到ViewModel中相同类型的枚举,但是然后它会从控件发送描述作为字符串并解析它(特别是如果本地化)非常麻烦? - gakera
终于找到解决方案了,谢谢。 - SkyDancer

2
这里有一个解决方案:您可以创建一个属性(列表),其中包含所有可能性,然后将您的ComboBox绑定到该属性。 在XAML中:
<ComboBox
    Name="ScopeComboBox" 
    Width="120" 
    Height="23" 
    Margin="12" 
    ItemsSource="{Binding Path=AccountDetailsProperty}"
    DisplayMemberPath="Value"/>

在代码后端:
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; }
}

Nicolas,感谢您的回复。我正在寻找更加面向XAML的解决方案,类似于:http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx - Boris

1
我会使用值转换器来实现这个,这将允许您直接使用转换器进行绑定,您可以更改Convert实现以使枚举的“更好”人类可读表示,例如在大写字符上拆分。
关于这种方法有一篇完整的文章在这里
  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 );
     }
  }


转换器是一个很好的想法。我该如何进行绑定? - Boris
@Boris:所有细节都在上面链接的文章中详细说明了。 - BrokenGlass

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接