2009-08-15 148 views
39

因此,web和StackOverflow對於如何將組合框綁定到WPF中的enum屬性有很多不錯的答案。但Silverlight是丟失了所有的使這成爲可能:(特點,例如:綁定ComboBoxes枚舉...在Silverlight!

  1. 不能使用通用EnumDisplayer風格IValueConverter接受一個類型參數,因爲Silverlight不支持x:Type
  2. this approach不能使用ObjectDataProvider一樣,因爲它沒有在Silverlight中存在。
  3. 不能從#2鏈接的評論中使用自定義標記擴展等中,由於標記擴展別t存在於Silverlight中。
  4. 您不能使用泛型i的#1版本而不是該對象的Type屬性,因爲XAML不支持泛型(並且使它們工作的黑客都依賴於標記擴展,Silverlight不支持)。

大規模失敗!

依我之見,使這項工作的唯一辦法是,要麼

  1. 作弊並綁定到我的視圖模型,其制定者/吸氣執行轉換的字符串屬性,裝載值到組合框使用代碼隱藏在視圖中。
  2. 爲我想綁定的每個枚舉定製IValueConverter

是否有任何更通用的替代方法,即不需要反覆爲每個我想要的枚舉重複寫入相同的代碼?我想我可以做的解決方案#2使用泛型類接受作爲枚舉類型參數,然後爲每一個枚舉我想這只是

class MyEnumConverter : GenericEnumConverter<MyEnum> {} 

你有什麼想法,球員創建新類?

回答

35

啊,我說得太快了!有a perfectly good solution,至少在Silverlight 3(它可能只是在3,因爲this thread表明,與此相關的東西,一個錯誤是固定在Silverlight 3)

基本上,你需要爲ItemsSource財產單轉換器,但它可以是完全通用的,而不使用任何禁止的方法,只要您傳遞其類型爲MyEnum的屬性的名稱即可。數據綁定到SelectedItem完全無痛;不需要轉換器!至少,只要你不想爲每個枚舉值通過例如自定義字符串。 DescriptionAttribute,嗯......可能需要另一個轉換器;希望我能把它變成通用的。

更新:我做了一個轉換器,它的工作原理!不幸的是,我現在必須綁定到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會變慢,所以我應該考慮使用緩存?

+0

絕對緩存所有你與Type對象做(即GetFields()),因爲它的反射和一般被認爲是緩慢的(當然這取決於東西你的應用程序使用反射)。除了那個不錯的工作! – 2009-08-17 17:35:56

+0

非常有幫助。謝謝。你有沒有想過把它擴展到容易翻譯的值 - 例如OrderStatus.NewOrder到「New Order」? – 2009-12-21 08:12:45

+0

的確,上面的代碼會解析你添加到枚舉字段中的任何'DescriptionAttributes'。 – Domenic 2009-12-21 12:35:18

5

我覺得場景枚舉數據的簡單封裝更容易使用。

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}" /> 

,這將完全地消除轉換器的需求... :)

+0

大聲笑這只是手動創建一個轉換器,只適用於一個特定的枚舉。 – Domenic 2012-12-01 07:26:27

1

這裏是一個Windows 8.1/Windows手機通用的應用程序相同的設置,主要變化是: -

  • 缺少說明框架中的屬性(或者至少我找不到它)
  • 在反思如何工作(使用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; 
         } 
        } 
    }