首页 文章

将ComboBoxes绑定到枚举...在Silverlight中!

提问于
浏览
38

因此,web和StackOverflow对于如何将组合框绑定到WPF中的枚举属性有很多很好的答案 . 但是Silverlight缺少使这成为可能的所有功能:(例如:

  • 您不能使用接受类型参数的通用EnumDisplayer -style IValueConverter ,因为Silverlight不支持 x:Type .

  • 您不能像在this approach中那样使用 ObjectDataProvider ,因为它在Silverlight中不存在 .

  • 您可以在Silverlight中存在't use a custom markup extension like in the comments on the link from #2, since markup extensions don' .

  • 您不能使用泛型而不是对象的 Type 属性来执行#1版本,因为XAML中不支持泛型(并且使它们工作的hack全部依赖于标记扩展,Silverlight不支持) .

大规模的失败!

在我看来,做这项工作的唯一方法是

  • 作弊并绑定到我的ViewModel中的字符串属性,其setter / getter执行转换,使用View中的代码隐藏将值加载到ComboBox中 .

  • 为我要绑定的每个枚举创建一个自定义 IValueConverter .

是否有更通用的替代方案,即不要为我想要的每个枚举反复编写相同的代码?我想我可以使用接受枚举作为类型参数的泛型类来做解决方案#2,然后为我想要的每个枚举创建新类

class MyEnumConverter : GenericEnumConverter<MyEnum> {}

伙计们,你有什么想法?

4 回答

  • 0

    还有另一种方法可以将ComboBox绑定到枚举,而无需为所选项目定制转换器 . 你可以查看

    http://charlass.wordpress.com/2009/07/29/binding-enums-to-a-combobbox-in-silverlight/

    它不使用DescriptionAttributes ....但它对我来说很完美,所以我想这取决于它将被使用的场景

  • 34

    唉,我说得太快了!至少在Silverlight 3中有a perfectly good solution . (它可能只有3,因为this thread表示在Silverlight 3中修复了与此相关的错误 . )

    基本上,您需要为 ItemsSource 属性使用单个转换器,但它可以完全通用而不使用任何禁止的方法,只要您传递类型为 MyEnum 的属性的名称即可 . 数据绑定到 SelectedItem 完全没有痛苦;不需要转换器!好吧,至少只要您不想通过例如每个枚举值自定义字符串 . DescriptionAttribute ,嗯...可能还需要另一个转换器;希望我能把它变得通用 .

    Update: 我做了一个转换器,它的工作原理!遗憾的是,我现在必须绑定到 SelectedIndex ,但没关系 . 使用这些家伙:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Windows.Data;
    
    namespace DomenicDenicola.Wpf
    {
        public class EnumToIntConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                // Note: as pointed out by Martin in the comments on this answer, this line
                // depends on the enum values being sequentially ordered from 0 onward,
                // since combobox indices are done that way. A more general solution would
                // probably look up where in the GetValues array our value variable
                // appears, then return that index.
                return (int)value;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return Enum.Parse(targetType, value.ToString(), true);
            }
        }
        public class EnumToIEnumerableConverter : IValueConverter
        {
            private Dictionary<Type, List<object>> cache = new Dictionary<Type, List<object>>();
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                var type = value.GetType();
                if (!this.cache.ContainsKey(type))
                {
                    var fields = type.GetFields().Where(field => field.IsLiteral);
                    var values = new List<object>();
                    foreach (var field in fields)
                    {
                        DescriptionAttribute[] a = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
                        if (a != null && a.Length > 0)
                        {
                            values.Add(a[0].Description);
                        }
                        else
                        {
                            values.Add(field.GetValue(value));
                        }
                    }
                    this.cache[type] = values;
                }
    
                return this.cache[type];
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }
    

    有了这种绑定XAML:

    <ComboBox x:Name="MonsterGroupRole"
              ItemsSource="{Binding MonsterGroupRole,
                                    Mode=OneTime,
                                    Converter={StaticResource EnumToIEnumerableConverter}}"
              SelectedIndex="{Binding MonsterGroupRole,
                                      Mode=TwoWay,
                                      Converter={StaticResource EnumToIntConverter}}" />
    

    这种资源声明XAML:

    <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:ddwpf="clr-namespace:DomenicDenicola.Wpf">
        <Application.Resources>
            <ddwpf:EnumToIEnumerableConverter x:Key="EnumToIEnumerableConverter" />
            <ddwpf:EnumToIntConverter x:Key="EnumToIntConverter" />
        </Application.Resources>
    </Application>
    

    任何评论将不胜感激,因为我有点像XAML / Silverlight / WPF /等 . 新手 . 例如, EnumToIntConverter.ConvertBack 会慢,所以我应该考虑使用缓存吗?

  • 4

    我发现枚举数据的简单封装更容易使用 .

    public ReadOnly property MonsterGroupRole as list(of string)
      get
        return [Enum].GetNames(GetType(GroupRoleEnum)).Tolist
      End get
    End Property
    
    private _monsterEnum as GroupRoleEnum
    Public Property MonsterGroupRoleValue as Integer
      get
        return _monsterEnum
      End get
      set(value as integer)
        _monsterEnum=value
      End set
    End Property
    

    ...

    <ComboBox x:Name="MonsterGroupRole"
          ItemsSource="{Binding MonsterGroupRole,
                                Mode=OneTime}"
          SelectedIndex="{Binding MonsterGroupRoleValue ,
                                  Mode=TwoWay}" />
    

    这将完全取消转换器的需要...... :)

  • 4

    以下是Windows 8.1 / Windows Phone通用应用程序的相同设置,主要更改如下: -

    • 框架中缺少DescriptionAttribute(或者至少我找不到它)

    • 反射如何工作的差异(使用TypeInfo.Declared字段)

    似乎XAML的顺序也很重要,我不得不将ItemsSource放在SelectedIndex之前,否则它不会调用ItemsSource绑定,例如

    <ComboBox
    ItemsSource="{Binding Path=MyProperty,Mode=OneWay, Converter={StaticResource EnumToIEnumerableConverter}}"
    SelectedIndex="{Binding Path=MyProperty, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}" 
    />
    

    代码如下

    namespace MyApp.Converters
    {
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Reflection;
        using Windows.UI.Xaml.Data;
        public class EnumToIntConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, string language)
            {
                // Note: as pointed out by Martin in the comments on this answer, this line
                // depends on the enum values being sequentially ordered from 0 onward,
                // since combobox indices are done that way. A more general solution would
                // probably look up where in the GetValues array our value variable
                // appears, then return that index.
                return (int) value;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, string language)
            {
                return value;
            }
        }
    
        public class EnumToIEnumerableConverter : IValueConverter
        {
            private readonly Dictionary<TypeInfo, List<object>> _cache = new Dictionary<TypeInfo, List<object>>();
    
            public object Convert(object value, Type targetType, object parameter, string language)
            {
                var type = value.GetType().GetTypeInfo();
                if (!_cache.ContainsKey(type))
                {
                    var fields = type.DeclaredFields.Where(field => field.IsLiteral);
                    var values = new List<object>();
                    foreach (var field in fields)
                    {
                        var a = (DescriptionAttribute[]) field.GetCustomAttributes(typeof(DescriptionAttribute), false);
                        if (a != null && a.Length > 0)
                        {
                            values.Add(a[0].Description);
                        }
                        else
                        {
                            values.Add(field.GetValue(value));
                        }
                    }
                    _cache[type] = values;
                }
                return _cache[type];
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, string language)
            {
                throw new NotImplementedException();
            }
        }
        [AttributeUsage(AttributeTargets.Field)]
        public class DescriptionAttribute : Attribute
        {
            public string Description { get; private set; }
    
            public DescriptionAttribute(string description)
            {
                Description = description;
            }
        }
    }
    

相关问题