【问题标题】:WPF Data binding: How to data bind an enum to combo box using XAML? [duplicate]WPF 数据绑定:如何使用 XAML 将枚举数据绑定到组合框? [复制]
【发布时间】:2011-05-17 10:39:57
【问题描述】:

我有一堂课:

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。我需要类似的东西。

【问题讨论】:

    标签: c# wpf xaml data-binding combobox


    【解决方案1】:

    一个非常简单的方法是使用 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>
    

    在转换器中,您可以将 Regex 用于 this 问题中的 CamelCase 字符串拆分器。我使用了最高级的版本,但您可能可以使用更简单的版本。其他细节 + 正则表达式 = 其他细节。降低返回值,然后返回第一个字符大写的字符串应该会给你预期的结果

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

    【讨论】:

    • 太棒了!谢谢,Meleak
    【解决方案2】:

    我一直这样做的方式如下。这个解决方案的美妙之处在于它是完全通用的,并且可以重复用于任何枚举类型。

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

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

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

    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) 现在只需将您的组合框的 ItemsSource 绑定到我们刚刚在资源字典中定义的这个键

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

    如果您使用我上面的枚举尝试此代码,您应该会在组合框中看到 3 个项目:

    Item 1
    Item 2
    Item3
    

    希望这会有所帮助。

    编辑: 如果您添加 LocalizableDescriptionAttribute 的实现并使用它来代替我使用的 Description 属性,那将是完美的。

    【讨论】:

    • 这是一个很好的方法。
    • 将 SelectedItem 绑定回视图模型的最佳方法是什么?我试图直接绑定到视图模型中相同类型的枚举,但随后它将描述作为从控件发送的字符串并解析它(尤其是在本地化时)很痛苦?
    • 终于找到解决办法了,谢谢。
    【解决方案3】:

    这是一个解决方案:创建一个包含所有可能性的属性(列表),然后将 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; }
    }
    

    【讨论】:

    【解决方案4】:

    我会为此使用值转换器,这将允许您直接使用转换器进行绑定,您可以更改转换实现以使枚举的“更好”人类可读表示,即拆分大写字符。

    有一篇关于这种方法的完整文章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 );
         }
      }
    

    【讨论】:

    • 转换器是个好主意。怎么绑定?
    • @Boris:上面链接的文章中都有详细说明
    猜你喜欢
    • 1970-01-01
    • 2010-09-08
    • 2018-11-12
    • 2020-02-07
    • 1970-01-01
    • 2019-02-19
    • 2011-08-04
    • 2015-08-20
    相关资源
    最近更新 更多