2009-08-15 3 views
39

तो, वेब, और स्टैक ओवरफ्लो, डब्ल्यूपीएफ में एक एनम संपत्ति के लिए एक combobox बांधने के लिए बहुत अच्छे जवाब है। लेकिन सिल्वरलाइट कि यह संभव :(बनाने सुविधाओं के सभी याद आ रही है उदाहरण के लिए:।बाध्यकारी कॉम्बोबॉक्सों को enums ... Silverlight में!

  1. आप एक सामान्य EnumDisplayer शैली IValueConverter है कि एक प्रकार पैरामीटर स्वीकार करता है उपयोग नहीं कर सकते, क्योंकि सिल्वरलाइट x:Type का समर्थन नहीं करता
  2. आप this approach में ObjectDataProvider उपयोग कर सकते हैं नहीं, की तरह है, क्योंकि यह Silverlight में मौजूद नहीं है।
  3. आप एक कस्टम मार्कअप एक्सटेंशन की तरह # 2 से लिंक पर टिप्पणी में, उपयोग नहीं कर सकते क्योंकि मार्कअप एक्सटेंशन डॉन ' टी सिल्वरलाइट में मौजूद नहीं है।
  4. आप जेनिक्स का उपयोग करके # 1 का संस्करण नहीं कर सकते। ऑब्जेक्ट के Type गुणों के nstead, चूंकि जेनिक्स XAML में समर्थित नहीं हैं (और हैक उन्हें काम करने के लिए सभी मार्कअप एक्सटेंशन पर निर्भर करते हैं, सिल्वरलाइट में समर्थित नहीं हैं)।

भारी असफल!

मैं इसे देखना है, यह काम करने के लिए एक ही रास्ता करने के लिए या तो

  1. Cheat और मेरे ViewModel, जिसका सेटर/गेटर रूपांतरण करता है में एक स्ट्रिंग संपत्ति को बाँध, ComboBox में लोड हो रहा है मानों का उपयोग करते है दृश्य में कोड-पीछे।
  2. प्रत्येक enum के लिए एक कस्टम IValueConverter बनाएं जो मैं बांधना चाहता हूं।

क्या कोई विकल्प है जो अधिक सामान्य हैं, यानी मैं हर एनम के लिए एक ही कोड लिखना शामिल नहीं करता हूं? मुझे लगता है मैं एक सामान्य वर्ग एक प्रकार पैरामीटर के रूप में स्वीकार करने enum का उपयोग कर # 2 समाधान कर सकता है, और उसके बाद हर enum मैं चाहता हूँ कि बस

class MyEnumConverter : GenericEnumConverter<MyEnum> {} 

क्या हैं अपने विचारों, लोग कर रहे हैं के लिए नए वर्गों बनाने लगता है?

उत्तर

35

एघ, मैंने बहुत जल्द बात की! वहाँ a perfectly good solution, कम से कम सिल्वरलाइट 3. में है (यह केवल 3 में हो सकता है, के बाद से this thread इंगित करता है कि इस सामग्री से संबंधित एक बग सिल्वरलाइट में तय किया गया था 3.)

मूल रूप से, आप ItemsSource संपत्ति के लिए एक एकल कनवर्टर की जरूरत है , लेकिन यह किसी भी प्रतिबंधित विधियों का उपयोग किए बिना पूरी तरह से सामान्य हो सकता है, जब तक कि आप इसे उस संपत्ति का नाम पारित करें जिसका प्रकार MyEnum है। और SelectedItem पर डाटाबेसिंग पूरी तरह से दर्द रहित है; कोई कनवर्टर की जरूरत नहीं है! खैर, कम से कम यह तब तक है जब तक आप प्रत्येक enum मान के लिए कस्टम स्ट्रिंग नहीं चाहते हैं उदा। DescriptionAttribute, हम्म ... शायद उस के लिए एक और कनवर्टर की आवश्यकता होगी; उम्मीद है कि मैं इसे सामान्य बना सकता हूं।

अद्यतन: मैंने कनवर्टर बनाया और यह काम करता है! मुझे अब SelectedIndex से बांधना है, दुख की बात है, लेकिन यह ठीक है।इन लोगों का उपयोग करें:

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

और संसाधन-घोषणा XAML इस तरह की:

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 की इस तरह के साथ

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

किसी भी टिप्पणी की सराहना की जाएगी, के रूप में मैं कुछ हद तक एक्सएएमएल/सिल्वरलाइट/डब्ल्यूपीएफ/आदि। नौसिखिया। उदाहरण के लिए, EnumToIntConverter.ConvertBack धीमा हो जाएगा, ताकि मुझे कैश का उपयोग करने पर विचार करना चाहिए?

+0

निश्चित रूप से सभी सामग्री प्रकार वस्तु के साथ कर रहे हैं (यानी GetFields()) के रूप में यह प्रतिबिंब है और आम तौर पर धीमी गति से माना जाता (हालांकि निश्चित रूप उस पर निर्भर कैश आपके आवेदन का प्रतिबिंब का उपयोग)। उस अच्छे काम के अलावा! –

+0

बहुत उपयोगी है। धन्यवाद। क्या आपने कभी भी मूल्यों के आसान अनुवाद के लिए इसे विस्तारित करने के लिए गोल किया है - जैसे ऑर्डरस्टैटस.NewOrder "नया ऑर्डर"? –

+0

दरअसल, उपर्युक्त कोड एनम फ़ील्ड्स में जोड़े गए किसी भी 'विवरण एट्रिब्यूट्स' को पार्स करेगा :)। – Domenic

5

चयनित आइटम के लिए कस्टम कनवर्टर की आवश्यकता के बिना कॉम्बोबॉक्स को एनम्स पर बांधने का एक और तरीका है। आप

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

पर यह जांच कर सकते हैं यह DescriptionAttributes उपयोग नहीं करता है .... लेकिन यह मेरे लिए पूरी तरह से काम करता है, तो मैं यह परिदृश्य यह

+0

उपयोगी लिंक लेकिन अगर लिंक टूटा हुआ है तो जवाब में महत्वपूर्ण भागों को शामिल करना बेहतर है। – Fedor

5

मुझे लगता है उपयोग किया जाएगा पर निर्भर करता है लगता है कि enum डेटा का एक सरल encapsulation उपयोग करने के लिए रास्ता आसान है।

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

lol यह मैन्युअल रूप से एक कनवर्टर बना रहा है जो केवल एक विशिष्ट enum के लिए काम करता है। – Domenic

1

यहाँ एक Windows 8.1/विंडोज फोन सार्वभौमिक अनुप्रयोग के लिए एक ही सेटअप है , मुख्य परिवर्तन हैं: -

  • गुम विवरण ढांचे में विशेषता (या कम से कम मुझे यह नहीं मिल रहा है)
  • कैसे काम करता है प्रतिबिंब (TypeInfo.Declared फ़ील्ड का उपयोग करके)

ऐसा लगता है कि XAML के आदेश भी महत्वपूर्ण है में

  • मतभेद, मैं SelectedIndex से पहले ItemsSource डाल करने के लिए अन्यथा यह 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; 
         } 
        } 
    }