WPF में एक कॉम्बोक्स नियंत्रण के लिए एक एनम कैसे बांधें?


182

मैं एक सरल उदाहरण खोजने की कोशिश कर रहा हूं जहां एनम दिखाए गए हैं। मैंने जो भी उदाहरण देखे हैं, वे अच्छे दिखने वाले प्रदर्शन तार जोड़ने की कोशिश करते हैं, लेकिन मैं उस जटिलता को नहीं चाहता।

मूल रूप से मेरे पास एक वर्ग है जो सभी गुण रखता है जिसे मैं बाँधता हूँ, पहले DataContext को इस वर्ग में सेट करके, और फिर xaml फ़ाइल में इस तरह बंधन को निर्दिष्ट करता हूँ:

<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>

लेकिन यह आयम मानों ComboBoxको आइटम के रूप में नहीं दिखाता है ।


9
यहाँ आप क्या देख रहे हैं: WPF ObjectDataProvider - ComboBox के लिए बाध्यकारी Enum आप वहाँ से पूरा स्रोत कोड उदाहरण भी डाउनलोड कर सकते हैं।

मेरी राय में सबसे अच्छा जवाब में है: stackoverflow.com/questions/58743/…
जिम्पी

जवाबों:


306

आप निम्न कोड को विंडो Loadedईवेंट हैंडलर में निम्न कोड रखकर कर सकते हैं , उदाहरण के लिए:

yourComboBox.ItemsSource = Enum.GetValues(typeof(EffectStyle)).Cast<EffectStyle>();

यदि आपको इसे XAML में बांधने की आवश्यकता है तो आपको ObjectDataProviderबाध्यकारी स्रोत के रूप में उपलब्ध वस्तु को बनाने के लिए उपयोग करने की आवश्यकता है :

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:System="clr-namespace:System;assembly=mscorlib"
        xmlns:StyleAlias="clr-namespace:Motion.VideoEffects">
    <Window.Resources>
        <ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
                            ObjectType="{x:Type System:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="StyleAlias:EffectStyle"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>
    <Grid>
        <ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}"
                  SelectedItem="{Binding Path=CurrentEffectStyle}" />
    </Grid>
</Window>

अगले कोड पर ध्यान आकर्षित करें:

xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects"

गाइड कैसे नामस्थान और विधानसभा आप MSDN पर पढ़ सकते हैं नक्शा करने के लिए ।


1
पहले लिंक से परीक्षण किया गया उदाहरण, ठीक काम करता है। जोड़ा गया कोड देखें और मेरे उत्तर में टिप्पणी करें।
Kyrylo M

1
MSDN फ़ोरम ( social.msdn.microsoft.com/Forums/en/wpf/thread/… ) पर अपनी समस्या का पता लगाएं । प्रोजेक्ट को साफ़ और पुनर्निर्माण करने का प्रयास करें। संभवतः आपको एक अन्य प्रश्न पर यहाँ उस समस्या के लिए पूछना चाहिए। यह वही है जो मैं सलाह दे सकता हूं ... वैसे भी, दिखाया गया उदाहरण सही है।
Kyrylo M

1
धन्यवाद, यह विचित्र है, लेकिन मैंने wpf पागलपन के साथ समान सामान देखा है। करेंगे और आपको बताएंगे। Btw यह वही समस्या है जो यहाँ वर्णित है: social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
Joan Venge

2
इसे xmlns:DllAlias="clr-namespace:NamespaceInsideDll; assembly=DllAssemblyName"उपयोग करने के लिए आपका संदर्भ जोड़ना होगा और XAML में जोड़ना होगा। यहाँ गाइड है: msdn.microsoft.com/en-us/library/ms747086.aspx
Kyrylo M

4
आप ReSharper जैसे टूल का उपयोग कर सकते हैं। यह सभी संदर्भित विधानसभाओं को पार्स करता है और सुझाव देता है कि क्या शामिल करने की आवश्यकता है। लिखने की जरूरत नहीं है - बस विकल्पों में से चयन करें।
Kyrylo एम

116

मुझे उन सभी वस्तुओं के लिए पसंद है जिन्हें मैं अपने में परिभाषित करने के लिए बाध्य कर रहा हूं ViewModel, इसलिए <ObjectDataProvider>जब संभव हो तो मैं xaml के उपयोग से बचने की कोशिश करता हूं ।

मेरा समाधान दृश्य में परिभाषित कोई डेटा और कोई कोड-पीछे का उपयोग करता है। केवल एक DataBinding, एक पुन: प्रयोज्य ValueConverter, किसी भी Enum प्रकार के लिए विवरण का एक संग्रह प्राप्त करने के लिए एक विधि, और देखने के लिए बाइंड करने के लिए ViewModel में एक एकल गुण।

जब मैं Enumकिसी ComboBoxऐसे पाठ में बाँधना चाहता हूँ जिसे मैं प्रदर्शित करना चाहता हूँ, के मूल्यों से कभी मेल नहीं खाता है Enum, इसलिए मैं [Description()]इसे उस पाठ को देने के लिए विशेषता का उपयोग करता हूँ जिसे मैं वास्तव में देखना चाहता हूँ ComboBox। यदि मेरे पास सप्ताह के दिनों की एक पहेली है, तो यह कुछ इस तरह दिखाई देगा:

public enum DayOfWeek
{
  // add an optional blank value for default/no selection
  [Description("")]
  NOT_SET = 0,
  [Description("Sunday")]
  SUNDAY,
  [Description("Monday")]
  MONDAY,
  ...
}

पहले मैंने शत्रुओं से निपटने के लिए कुछ तरीकों के साथ हेल्पर क्लास बनाई। एक विधि को एक विशिष्ट मूल्य के लिए विवरण मिलता है, दूसरी विधि में एक प्रकार के लिए सभी मूल्य और उनके विवरण मिलते हैं।

public static class EnumHelper
{
  public static string Description(this Enum value)
  {
    var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
    if (attributes.Any())
      return (attributes.First() as DescriptionAttribute).Description;

    // If no description is found, the least we can do is replace underscores with spaces
    // You can add your own custom default formatting logic here
    TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
    return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
  }

  public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
  {
    if (!t.IsEnum)
      throw new ArgumentException($"{nameof(t)} must be an enum type");

    return Enum.GetValues(t).Cast<Enum>().Select((e) => new ValueDescription() { Value = e, Description = e.Description() }).ToList();
  }
}

अगला, हम एक बनाते हैं ValueConverter। इनहेरिट MarkupExtensionकरना XAML में उपयोग करना आसान बनाता है इसलिए हमें इसे संसाधन के रूप में घोषित करने की आवश्यकता नहीं है।

[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return EnumHelper.GetAllValuesAndDescriptions(value.GetType());
  }
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return null;
  }
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    return this;
  }
}

मेरे ViewModelलिए केवल 1 संपत्ति की जरूरत है कि मेरे Viewदोनों के लिए करने के लिए बाध्य कर सकते हैं SelectedValueऔर ItemsSourceबता गया की:

private DayOfWeek dayOfWeek;

public DayOfWeek SelectedDay
{
  get { return dayOfWeek; }
  set
  {
    if (dayOfWeek != value)
    {
      dayOfWeek = value;
      OnPropertyChanged(nameof(SelectedDay));
    }
  }
}

और अंत में बाध्य करने के लिए ComboBox(का उपयोग करते हुए देखने ValueConverterमें ItemsSourceबंधन) ...

<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
          SelectedValuePath="Value"
          DisplayMemberPath="Description"
          SelectedValue="{Binding Path=SelectedDay}" />

इस समाधान को लागू करने के लिए आपको केवल मेरी EnumHelperकक्षा और EnumToCollectionConverterकक्षा की प्रतिलिपि बनाने की आवश्यकता है । वे किसी भी एनम के साथ काम करेंगे । इसके अलावा, मैंने इसे यहां शामिल नहीं किया है, लेकिन ValueDescriptionवर्ग सिर्फ 2 सार्वजनिक वस्तु गुणों के साथ एक साधारण वर्ग है, एक को बुलाया जाता है Value, एक को बुलाया जाता है Description। आप खुद बना सकते हैं या आप कोड को बदल सकते हैं Tuple<object, object>या उपयोग करने के लिएKeyValuePair<object, object>


9
यह काम करने के लिए, मैं एक बनाने के लिए किया था ValueDescriptionवर्ग है जिसके लिए सार्वजनिक गुण है ValueऔरDescription
Perchik

4
हां, आप इस कोड को कक्षा के बजाय Tuple<T1, T2>या उसके बाद उपयोग करने के लिए बदल सकते हैं और फिर आपको अपना स्वयं का बनाना नहीं होगा। KeyValuePair<TKey, TValue>ValueDescription
निक

मुझे ViewModel गुणों के लिए OnPropertyChanged (या समतुल्य) को लागू करने की आवश्यकता थी, न कि केवल SelectClass की।
विल

आपको उस संपत्ति के लिए OnPropertyChanged को लागू करने की आवश्यकता नहीं होनी चाहिए जो सूची लौटाती है। सूची एक Enum में मानों से उत्पन्न होती है। यह रन टाइम के दौरान कभी नहीं बदलेगा, और जब यह कभी नहीं बदलता है, तो इसे कभी भी किसी को सूचित करने की आवश्यकता नहीं है कि यह बदल गया है। इसके अलावा, अद्यतन संस्करण के साथ, सूची संपत्ति की भी आवश्यकता नहीं है।
निक

कॉम्बोक्स का आइटम स्रोत और सिलेक्टवैल्यू एक ही संपत्ति कैसे है। क्या ItemSource को सूची बनाने की आवश्यकता नहीं है? ओह, मैं देखता हूं, यह bcuase है EnumHelper वस्तुओं की एक सूची बनाता है। यह वास्तव में मेरा ViewModel को सरल बनाता है क्योंकि मुझे ItemSource को पॉप्युलेट करने के लिए वस्तुओं की एक अलग सूची बनाए रखने की आवश्यकता नहीं है।
चुपके रब्बी

46

मैंने MarkupExtension का उपयोग करते हुए एक और समाधान का उपयोग किया।

  1. मैंने वर्ग बनाया जो आइटम स्रोत प्रदान करता है:

    public class EnumToItemsSource : MarkupExtension
    {
        private readonly Type _type;
    
        public EnumToItemsSource(Type type)
        {
            _type = type;
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return Enum.GetValues(_type)
                .Cast<object>()
                .Select(e => new { Value = (int)e, DisplayName = e.ToString() });
        }
    }
  2. यह लगभग सभी ... अब इसे XAML में उपयोग करें:

        <ComboBox DisplayMemberPath="DisplayName"
              ItemsSource="{persons:EnumToItemsSource {x:Type enums:States}}"
              SelectedValue="{Binding Path=WhereEverYouWant}"
              SelectedValuePath="Value" />
  3. Um एनम: स्टेट्स ’को अपनी एनम में बदलें


1
@ निक: स्वीकृत उत्तर xaml में भी एनम (या मॉडल जैसा आपने कहा) का संदर्भ है। आपका समाधान दृश्य मॉडल में 2 गुण और बैकिंग फ़ील्ड बना रहा है, जो मुझे पसंद नहीं आया (DRY नियम)। और हां, आपको e.ToString()प्रदर्शन नाम के लिए उपयोग नहीं करना है । आप जो भी हो, आप अपने खुद के अनुवादक, डिसक्रिटिपेशन विशेषता पार्सर का उपयोग कर सकते हैं।
tom.maruska

2
@ tom.maruska मैं आपके उत्तर बनाम आपके उत्तर में आने की कोशिश नहीं कर रहा हूं, लेकिन जब से आप इसे लाए हैं, 2 गुण होने से DRY नियम का उल्लंघन नहीं होता है जब वे 2 अलग-अलग गुण होते हैं जो विभिन्न उद्देश्यों की पूर्ति करते हैं। और आपके उत्तर के लिए भी एक संपत्ति जोड़ने की आवश्यकता होगी (आप स्वयं भी इस संपत्ति को कहते हैं {Binding Path=WhereEverYouWant}) और यदि आप चाहते हैं कि यह 2-वे बाइंडिंग का समर्थन करता है तो आप इसके लिए एक समर्थन क्षेत्र भी बनाने जा रहे हैं। तो आप ऐसा करने से 2 संपत्तियों और 1 समर्थन फ़ील्ड की जगह नहीं ले रहे हैं, आप केवल 1 एकल-पंक्ति आसानी से संपत्ति की जगह ले रहे हैं।
निक

@ ठीक है, आप उस संपत्ति और बैकिंग फील्ड के बारे में सही हैं :)
tom.maruska

24

ObjectDataProvider का उपयोग करें:

<ObjectDataProvider x:Key="enumValues"
   MethodName="GetValues" ObjectType="{x:Type System:Enum}">
      <ObjectDataProvider.MethodParameters>
           <x:Type TypeName="local:ExampleEnum"/>
      </ObjectDataProvider.MethodParameters>
 </ObjectDataProvider>

और फिर स्थिर संसाधन से बंधे:

ItemsSource="{Binding Source={StaticResource enumValues}}"

इस लेख के आधार पर


4
बिलकुल सरल उपाय। निर्मिर के जवाब में सिस्टम के लिए नेमस्पेस:xmlns:System="clr-namespace:System;assembly=mscorlib"
जोनाथन ट्विट

विजुअल स्टूडियो 2017 के WPF प्रोजेक्ट्स में अच्छी तरह से काम करता है।
सोरुष

10

निक के जवाब ने वास्तव में मेरी मदद की है, लेकिन मुझे एहसास हुआ कि इसे थोड़ा अलग किया जा सकता है, एक अतिरिक्त वर्ग, वैल्यूडेस्क्रिप्शन से बचने के लिए। मुझे याद आया कि फ्रेमवर्क में पहले से ही KeyValuePair क्लास मौजूद है, इसलिए इसका उपयोग किया जा सकता है।

कोड केवल थोड़ा बदलता है:

public static IEnumerable<KeyValuePair<string, string>> GetAllValuesAndDescriptions<TEnum>() where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        if (!typeof(TEnum).IsEnum)
        {
            throw new ArgumentException("TEnum must be an Enumeration type");
        }

        return from e in Enum.GetValues(typeof(TEnum)).Cast<Enum>()
               select new KeyValuePair<string, string>(e.ToString(),  e.Description());
    }


public IEnumerable<KeyValuePair<string, string>> PlayerClassList
{
   get
   {
       return EnumHelper.GetAllValuesAndDescriptions<PlayerClass>();
   }
}

और अंत में XAML:

<ComboBox ItemSource="{Binding Path=PlayerClassList}"
          DisplayMemberPath="Value"
          SelectedValuePath="Key"
          SelectedValue="{Binding Path=SelectedClass}" />

मुझे उम्मीद है कि यह दूसरों के लिए उपयोगी है।


मेरे पहले कार्यान्वयन का उपयोग नहीं किया गया था, KeyValuePairलेकिन अंत में मैंने निर्णय लिया KeyValuePairकि एक ऐसी चीज़ का प्रतिनिधित्व करने के लिए जो एक महत्वपूर्ण मूल्य जोड़ी नहीं है, केवल एक तुच्छ सरल वर्ग लिखने से बचने के लिए एक टन का अर्थ नहीं था। ValueDescriptionवर्ग केवल 5 लाइनों है, और उनमें से 2 बस रहे हैं {और}
निक

8

आपको एनम में मानों की एक सरणी बनाने की आवश्यकता होगी, जिसे System.Enum.GetValues ​​() कहकर बनाया जा सकता है , यह उस एनम के पास से गुजरता है Typeजिसे आप आइटम चाहते हैं।

यदि आप ItemsSourceइसे संपत्ति के लिए निर्दिष्ट करते हैं, तो इसे सभी एनम के मूल्यों के साथ आबाद किया जाना चाहिए। आप शायद बाध्य करने के लिए चाहते SelectedItemकरने के लिए EffectStyle(यह मानते हुए यह एक ही enum की संपत्ति है, और वर्तमान मूल्य शामिल हैं)।


धन्यवाद, क्या आप कृपया कोड में पहला भाग दिखा सकते हैं? मुझे यकीन नहीं है कि एनम मूल्यों को सरणी के रूप में कहाँ संग्रहीत किया जाए? Enum प्रॉपर्टी दूसरी श्रेणी में स्थित है। क्या मैं इस GetValues ​​को xaml के अंदर कर सकता हूं?
जोन वेंज

4

उपरोक्त सभी पोस्ट एक सरल ट्रिक से चूक गए हैं। यह सेलेक्टवैल्यू के बंधन से यह पता लगाना संभव है कि आइटम्स को स्वचालित रूप से कैसे पॉपअप करें ताकि आपका एक्सएएमएल मार्कअप बस हो जाए।

<Controls:EnumComboBox SelectedValue="{Binding Fool}"/>

मेरे ViewModel में उदाहरण के लिए मेरे पास है

public enum FoolEnum
    {
        AAA, BBB, CCC, DDD

    };


    FoolEnum _Fool;
    public FoolEnum Fool
    {
        get { return _Fool; }
        set { ValidateRaiseAndSetIfChanged(ref _Fool, value); }
    }

ValidateRaiseAndSetIfChanged मेरा INPC हुक है। तुम्हारा अलग हो सकता है।

EnumComboBox का कार्यान्वयन निम्नानुसार है, लेकिन पहले मुझे अपने एन्यूमरेशन स्ट्रिंग्स और मूल्यों को प्राप्त करने के लिए थोड़ा सहायक की आवश्यकता होगी

    public static List<Tuple<object, string, int>> EnumToList(Type t)
    {
        return Enum
            .GetValues(t)
            .Cast<object>()
            .Select(x=>Tuple.Create(x, x.ToString(), (int)x))
            .ToList();
    }

और मुख्य वर्ग (नोट जब मैं संपत्ति के परिवर्तन के लिए ReactiveUI का उपयोग कर रहा हूं जब व्हेनए के माध्यम से)

using ReactiveUI;
using ReactiveUI.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Documents;

namespace My.Controls
{
    public class EnumComboBox : System.Windows.Controls.ComboBox
    {
        static EnumComboBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(EnumComboBox), new FrameworkPropertyMetadata(typeof(EnumComboBox)));
        }

        protected override void OnInitialized( EventArgs e )
        {
            base.OnInitialized(e);

            this.WhenAnyValue(p => p.SelectedValue)
                .Where(p => p != null)
                .Select(o => o.GetType())
                .Where(t => t.IsEnum)
                .DistinctUntilChanged()
                .ObserveOn(RxApp.MainThreadScheduler)
                .Subscribe(FillItems);
        }

        private void FillItems(Type enumType)
        {
            List<KeyValuePair<object, string>> values = new List<KeyValuePair<object,string>>();

            foreach (var idx in EnumUtils.EnumToList(enumType))
            {
                values.Add(new KeyValuePair<object, string>(idx.Item1, idx.Item2));
            }

            this.ItemsSource = values.Select(o=>o.Key.ToString()).ToList();

            UpdateLayout();
            this.ItemsSource = values;
            this.DisplayMemberPath = "Value";
            this.SelectedValuePath = "Key";

        }
    }
}

आपको जेनेरिक.एक्सएएमएल में शैली को सही ढंग से सेट करने की आवश्यकता है या आपका बॉक्स कुछ भी प्रस्तुत नहीं करेगा और आप अपने बालों को बाहर खींच लेंगे।

<Style TargetType="{x:Type local:EnumComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
</Style>

और वह यह है कि यह स्पष्ट रूप से i18n का समर्थन करने के लिए बढ़ाया जा सकता है, लेकिन पोस्ट को लंबे समय तक बना देगा।


3

यूनिवर्सल ऐप्स कुछ अलग तरह से काम करते हैं; इसमें पूर्ण विशेषताओं वाले XAML की सारी शक्ति नहीं है। मेरे लिए क्या काम किया है:

  1. मैंने एनम वैल्यूज़ की एक सूची एनमों के रूप में बनाई थी (स्ट्रिंग्स में या पूर्णांक में परिवर्तित नहीं हुई) और कॉम्बो बॉक्स आइटम स्रोत के लिए बाध्य किया
  2. तब मैं अपनी सार्वजनिक संपत्ति पर ComboBox ItemSelected बाँध सकता था जिसका प्रकार प्रश्न में एनम है

बस मज़े के लिए मैंने इसमें मदद करने के लिए थोड़ा टेम्प्लेटेड क्लास को मार दिया और इसे MSDN नमूने के पन्नों में प्रकाशित किया । अतिरिक्त बिट्स मुझे वैकल्पिक रूप से एनमों के नाम को ओवरराइड करने और मुझे कुछ एनमों को छिपाने की अनुमति देते हैं। मेरा कोड निक (ऊपर) की तरह एक भयानक लगता है, जो मुझे लगता है कि मैंने पहले देखा था।

नमूना चल रहा है;  इसमें Enum में कई twoway बाइंडिंग शामिल हैं


3

इस सवाल के कई बेहतरीन जवाब हैं और मैं विनम्रता से अपनी बात प्रस्तुत करता हूं। मुझे लगता है कि मेरा कुछ सरल और अधिक सुरुचिपूर्ण है। इसके लिए केवल एक मान कनवर्टर की आवश्यकता है।

एक Enum दिया ...

public enum ImageFormat
{
    [Description("Windows Bitmap")]
    BMP,
    [Description("Graphics Interchange Format")]
    GIF,
    [Description("Joint Photographic Experts Group Format")]
    JPG,
    [Description("Portable Network Graphics Format")]
    PNG,
    [Description("Tagged Image Format")]
    TIFF,
    [Description("Windows Media Photo Format")]
    WDP
}

और एक मान कनवर्टर ...

public class ImageFormatValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is ImageFormat format)
        {
            return GetString(format);
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is string s)
        {
            return Enum.Parse(typeof(ImageFormat), s.Substring(0, s.IndexOf(':')));
        }
        return null;
    }

    public string[] Strings => GetStrings();

    public static string GetString(ImageFormat format)
    {
        return format.ToString() + ": " + GetDescription(format);
    }

    public static string GetDescription(ImageFormat format)
    {
        return format.GetType().GetMember(format.ToString())[0].GetCustomAttribute<DescriptionAttribute>().Description;

    }
    public static string[] GetStrings()
    {
        List<string> list = new List<string>();
        foreach (ImageFormat format in Enum.GetValues(typeof(ImageFormat)))
        {
            list.Add(GetString(format));
        }

        return list.ToArray();
    }
}

संसाधनों ...

    <local:ImageFormatValueConverter x:Key="ImageFormatValueConverter"/>

XAML की घोषणा ...

    <ComboBox Grid.Row="9" ItemsSource="{Binding Source={StaticResource ImageFormatValueConverter}, Path=Strings}"
              SelectedItem="{Binding Format, Converter={StaticResource ImageFormatValueConverter}}"/>

देखें मॉडल ...

    private ImageFormat _imageFormat = ImageFormat.JPG;
    public ImageFormat Format
    {
        get => _imageFormat;
        set
        {
            if (_imageFormat != value)
            {
                _imageFormat = value;
                OnPropertyChanged();
            }
        }
    }

परिणामी कॉम्बोबॉक्स ...

ComboBox Enum के लिए बाध्य


मेरे लिए, यह सवाल का सबसे अच्छा समाधान है: सरल, समझने में आसान, लागू करने के लिए सीधा।
सूचित जनक

इस समाधान के साथ समस्या यह है कि यह अप्राप्य है।
रॉबिन डेविस

@RobinDavies आप इसे स्थानीय कर सकते हैं। एक कस्टम विवरण की आवश्यकता है जिसमें से कुछ को मैंने बनाया है। कुछ विचारों के लिए यह SO प्रश्न देखें: stackoverflow.com/questions/7398653/…
AQuirky

2
public class EnumItemsConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!value.GetType().IsEnum)
            return false;

        var enumName = value.GetType();
        var obj = Enum.Parse(enumName, value.ToString());

        return System.Convert.ToInt32(obj);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Enum.ToObject(targetType, System.Convert.ToInt32(value));
    }
}

इस तरह के एनम वैल्यू कन्वर्टर के साथ आपको रोजर्स और ग्रेग के उत्तर का विस्तार करना चाहिए, यदि आप ऑब्जेक्ट मॉडल गुणों को एनम के लिए सीधा बांध रहे हैं।


1

यदि आप अपने ViewModel पर एक वास्तविक एनम संपत्ति के लिए बाध्य कर रहे हैं, न कि एक एनम के एक int प्रतिनिधित्व, चीजें मुश्किल हो। मैंने पाया कि स्ट्रिंग के प्रतिनिधित्व के लिए बाध्य होना आवश्यक है, उपरोक्त सभी उदाहरणों में जैसा अपेक्षित है वैसा इंट वैल्यू नहीं।

यदि आप अपने ViewModel पर जिस संपत्ति को बांधना चाहते हैं, उसे एक साधारण टेक्स्टबॉक्स बांधकर बता सकते हैं। यदि यह पाठ दिखाता है, तो स्ट्रिंग को बांधें। यदि यह एक संख्या दिखाता है, तो मान को बांधें। नोट मैंने दो बार डिस्प्ले का उपयोग किया है जो सामान्य रूप से एक त्रुटि होगी, लेकिन यह एकमात्र तरीका है जिससे यह काम करता है।

<ComboBox SelectedValue="{Binding ElementMap.EdiDataType, Mode=TwoWay}"
                      DisplayMemberPath="Display"
                      SelectedValuePath="Display"
                      ItemsSource="{Binding Source={core:EnumToItemsSource {x:Type edi:EdiDataType}}}" />

ग्रेग


यह उत्तर अधूरा लगता है: * क्या है / कोर /?
ट्रेपिकि

1

मुझे tom.maruska का उत्तर पसंद आया , लेकिन मुझे किसी भी प्रकार का समर्थन करने की आवश्यकता थी, जो मेरे टेम्पलेट रनटाइम पर आ सकती है। उसके लिए, मुझे मार्कअप एक्सटेंशन के प्रकार को निर्दिष्ट करने के लिए एक बाध्यकारी का उपयोग करना पड़ा। मैं इस उत्तर में काम करने में सक्षम था nicolay.anykienko से एक बहुत ही लचीले मार्कअप एक्सटेंशन के साथ आने के लिए जो किसी भी मामले में मेरे विचार से काम करेगा। यह इस तरह से सेवन किया जाता है:

<ComboBox SelectedValue="{Binding MyEnumProperty}" 
          SelectedValuePath="Value"
          ItemsSource="{local:EnumToObjectArray SourceEnum={Binding MyEnumProperty}}" 
          DisplayMemberPath="DisplayName" />

ऊपर संदर्भित मार्कअप एक्सटेंशन के लिए स्रोत:

class EnumToObjectArray : MarkupExtension
{
    public BindingBase SourceEnum { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        DependencyObject targetObject;
        DependencyProperty targetProperty;

        if (target != null && target.TargetObject is DependencyObject && target.TargetProperty is DependencyProperty)
        {
            targetObject = (DependencyObject)target.TargetObject;
            targetProperty = (DependencyProperty)target.TargetProperty;
        }
        else
        {
            return this;
        }

        BindingOperations.SetBinding(targetObject, EnumToObjectArray.SourceEnumBindingSinkProperty, SourceEnum);

        var type = targetObject.GetValue(SourceEnumBindingSinkProperty).GetType();

        if (type.BaseType != typeof(System.Enum)) return this;

        return Enum.GetValues(type)
            .Cast<Enum>()
            .Select(e => new { Value=e, Name = e.ToString(), DisplayName = Description(e) });
    }

    private static DependencyProperty SourceEnumBindingSinkProperty = DependencyProperty.RegisterAttached("SourceEnumBindingSink", typeof(Enum)
                       , typeof(EnumToObjectArray), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));

    /// <summary>
    /// Extension method which returns the string specified in the Description attribute, if any.  Oherwise, name is returned.
    /// </summary>
    /// <param name="value">The enum value.</param>
    /// <returns></returns>
    public static string Description(Enum value)
    {
        var attrs = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attrs.Any())
            return (attrs.First() as DescriptionAttribute).Description;

        //Fallback
        return value.ToString().Replace("_", " ");
    }
}

1

सरल और स्पष्ट विवरण: http://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/

xmlns:local="clr-namespace:BindingEnums"
xmlns:sys="clr-namespace:System;assembly=mscorlib"

...

<Window.Resources>
    <ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
                        ObjectType="{x:Type sys:Enum}">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="local:Status"/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

...

<Grid>
    <ComboBox HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="150"
              ItemsSource="{Binding Source={StaticResource dataFromEnum}}"/>
</Grid>

0

का उपयोग करते हुए ReactiveUI, मैंने निम्नलिखित वैकल्पिक समाधान बनाया है। यह एक सुंदर ऑल-इन-वन समाधान नहीं है, लेकिन मुझे लगता है कि बहुत कम से कम यह पठनीय है।

मेरे मामले में, enumनियंत्रण की सूची को बांधना एक दुर्लभ मामला है, इसलिए मुझे कोड आधार पर समाधान को स्केल करने की आवश्यकता नहीं है। हालाँकि, कोड EffectStyleLookup.Itemको एक में बदलकर अधिक सामान्य बनाया जा सकता है Object। मैंने अपने कोड के साथ इसका परीक्षण किया, कोई अन्य संशोधन आवश्यक नहीं है। जिसका अर्थ है कि एक सहायक वर्ग को किसी भी enumसूची में लागू किया जा सकता है । हालांकि यह इसकी पठनीयता को कम करेगा - इसके ReactiveList<EnumLookupHelper>लिए एक शानदार रिंग नहीं है।

निम्नलिखित सहायक वर्ग का उपयोग करना:

public class EffectStyleLookup
{
    public EffectStyle Item { get; set; }
    public string Display { get; set; }
}

ViewModel में, enums की सूची में कनवर्ट करें और इसे एक संपत्ति के रूप में उजागर करें:

public ViewModel : ReactiveObject
{
  private ReactiveList<EffectStyleLookup> _effectStyles;
  public ReactiveList<EffectStyleLookup> EffectStyles
  {
    get { return _effectStyles; }
    set { this.RaiseAndSetIfChanged(ref _effectStyles, value); }
  }

  // See below for more on this
  private EffectStyle _selectedEffectStyle;
  public EffectStyle SelectedEffectStyle
  {
    get { return _selectedEffectStyle; }
    set { this.RaiseAndSetIfChanged(ref _selectedEffectStyle, value); }
  }

  public ViewModel() 
  {
    // Convert a list of enums into a ReactiveList
    var list = (IList<EffectStyle>)Enum.GetValues(typeof(EffectStyle))
      .Select( x => new EffectStyleLookup() { 
        Item = x, 
        Display = x.ToString()
      });

    EffectStyles = new ReactiveList<EffectStyle>( list );
  }
}

में ComboBox, का उपयोग SelectedValuePathमूल करने के लिए बाध्य करने के लिए संपत्ति, enumमूल्य:

<ComboBox Name="EffectStyle" DisplayMemberPath="Display" SelectedValuePath="Item" />

विचार में, यह मूल बाध्य करने के लिए हमें की अनुमति देता है enumके लिए SelectedEffectStyleViewModel में है, लेकिन प्रदर्शित ToString()में मूल्य ComboBox:

this.WhenActivated( d =>
{
  d( this.OneWayBind(ViewModel, vm => vm.EffectStyles, v => v.EffectStyle.ItemsSource) );
  d( this.Bind(ViewModel, vm => vm.SelectedEffectStyle, v => v.EffectStyle.SelectedValue) );
});

मुझे लगता है कि आपके ViewModel में त्रुटि है। 1) क्या यह EffectStyleLookup का ReactiveList नहीं होना चाहिए ?, 2) आपको पहले एक खाली ReactiveList <T> () बनाना चाहिए। फिर आइटम जोड़ें। अंत में: ReactiveList <T> अब पदावनत हो गया है (लेकिन अभी भी काम करता है)। EffectStyles = नया ReactiveList <EffectStyleLookup> (); EffectStyles.AddRange (सूची); यह दिखाने के लिए समय निकालने के लिए धन्यवाद।
user1040323

0

मैं अपनी टिप्पणी (VB में, दुख की बात है, जोड़ रहा हूँ, लेकिन अवधारणा को आसानी से एक दिल की धड़कन में C # पर दोहराया जा सकता है), क्योंकि मुझे बस इसका संदर्भ देना था और किसी भी उत्तर को पसंद नहीं था क्योंकि वे बहुत जटिल थे। यह मुश्किल नहीं होना चाहिए।

इसलिए मैं एक आसान तरीका लेकर आया हूं। Enumerators को एक शब्दकोश में बांधें। कॉम्बोक्स के लिए उस शब्दकोष को बांधें।

मेरा कॉम्बोक्स:

<ComboBox x:Name="cmbRole" VerticalAlignment="Stretch" IsEditable="False" Padding="2" 
    Margin="0" FontSize="11" HorizontalAlignment="Stretch" TabIndex="104" 
    SelectedValuePath="Key" DisplayMemberPath="Value" />

मेरा कोड-पीछे। उम्मीद है, यह किसी और को बाहर करने में मदद करता है।

Dim tDict As New Dictionary(Of Integer, String)
Dim types = [Enum].GetValues(GetType(Helper.Enumerators.AllowedType))
For Each x As Helper.Enumerators.AllowedType In types
    Dim z = x.ToString()
    Dim y = CInt(x)
    tDict.Add(y, z)
Next

cmbRole.ClearValue(ItemsControl.ItemsSourceProperty)
cmbRole.ItemsSource = tDict

Kyrylo का उत्तर आपकी तुलना में बहुत सरल है - मुझे समझ नहीं आता कि इसके बारे में क्या जटिल है? कोड में उनके शून्य रूपांतरण की आवश्यकता है।
जॉनथॉन सुलिंगर

मैं अपने सारे तर्क एक्सएएमएल के हाथों में नहीं देना चाहता था। मैं अपना तर्क अपने तरीके से करना चाहता हूं (हमेशा सबसे अच्छा तरीका नहीं), लेकिन यह मुझे यह समझने की अनुमति देता है कि योजना के अनुसार कुछ कहां और क्यों नहीं हो रहा है। उनका व्यक्तित्व कम जटिल है, लेकिन तर्क करने के लिए XAML / WPF पर निर्भर करता है। मैं सिर्फ उसका प्रशंसक नहीं हूं। एक बिल्ली की त्वचा पर 10,000 तरीके, आप जानते हैं?
लाकी पोलिटिस

काफी उचित। मैं व्यक्तिगत रूप से पहले से ही निर्मित सुविधाओं का उपयोग करना पसंद करता हूं, मेरे लिए, लेकिन यह सिर्फ मेरी प्राथमिकता है;
जॉनथॉन सुलिंगर

जी श्रीमान! मैं पूरी तरह से समझ गया। मुझे वेब डेवलपमेंट से आने वाले सॉफ़्टवेयर डेवलपमेंट में मजबूर किया गया है। मैं WPF पर अप-टू-डेट नहीं रहा और मुझे जैसे-जैसे साथ जाना है, उसे सीखना पड़ा। मुझे अभी भी WPF / XAML नियंत्रणों की सभी पेचीदगियों की समझ नहीं है, और इसलिए मुझे समाधान की तुलना में अधिक हिचकी मिल रही है कि मैं चीजों को कैसे काम करने की उम्मीद करूंगा। लेकिन मैं इस बातचीत की सराहना करता हूं। इसने मुझे कुछ और शोध करने के लिए बनाया है।
लाकी पोलिटिस

0

निक के विलेय को और अधिक सरल बनाया जा सकता है, कुछ भी नहीं फैंसी के साथ, आपको केवल एक ही कनवर्टर की आवश्यकता होगी:

[ValueConversion(typeof(Enum), typeof(IEnumerable<Enum>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var r = Enum.GetValues(value.GetType());
        return r;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

आप इसके बाद जहाँ भी आप अपने कॉम्बो बॉक्स को दिखाना चाहते हैं उसका उपयोग करें

<ComboBox ItemsSource="{Binding PagePosition, Converter={converter:EnumToCollectionConverter}, Mode=OneTime}"  SelectedItem="{Binding PagePosition}" />

0

मैं इसे लागू करने की सिफारिश नहीं करूंगा क्योंकि यह उम्मीद है कि यह एक अच्छा समाधान प्रेरित कर सकता है।

मान लीजिए कि आपकी दुश्मनी फू है। तब आप ऐसा कुछ कर सकते हैं।

public class FooViewModel : ViewModel
{
    private int _fooValue;

    public int FooValue
    {
        get => _fooValue;
        set
        {
            _fooValue = value;
            OnPropertyChange();
            OnPropertyChange(nameof(Foo));
            OnPropertyChange(nameof(FooName));
        }
    }
    public Foo Foo 
    { 
        get => (Foo)FooValue; 
        set 
        { 
            _fooValue = (int)value;
            OnPropertyChange();
            OnPropertyChange(nameof(FooValue));
            OnPropertyChange(nameof(FooName));
        } 
    }
    public string FooName { get => Enum.GetName(typeof(Foo), Foo); }

    public FooViewModel(Foo foo)
    {
        Foo = foo;
    }
}

फिर Window.Loadविधि पर आप सभी एनमों को लोड कर सकते हैं, ObservableCollection<FooViewModel>जिसे आप कॉम्बोक्स के डेटाकोटेक्स्ट के रूप में सेट कर सकते हैं।


0

मैंने बस इसे सरल रखा। मैंने अपने ViewModel में enum मानों के साथ आइटमों की एक सूची बनाई:

public enum InputsOutputsBoth
{
    Inputs,
    Outputs,
    Both
}

private IList<InputsOutputsBoth> _ioTypes = new List<InputsOutputsBoth>() 
{ 
    InputsOutputsBoth.Both, 
    InputsOutputsBoth.Inputs, 
    InputsOutputsBoth.Outputs 
};

public IEnumerable<InputsOutputsBoth> IoTypes
{
    get { return _ioTypes; }
    set { }
}

private InputsOutputsBoth _selectedIoType;

public InputsOutputsBoth SelectedIoType
{
    get { return _selectedIoType; }
    set
    {
        _selectedIoType = value;
        OnPropertyChanged("SelectedIoType");
        OnSelectionChanged();
    }
}

मेरे xaml कोड में मुझे बस इसकी आवश्यकता है:

<ComboBox ItemsSource="{Binding IoTypes}" SelectedItem="{Binding SelectedIoType, Mode=TwoWay}">
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.