c# binding - Come associare un enum a un controllo a combobox in WPF?




7 Answers

Puoi farlo dal codice posizionando il seguente codice nel gestore di eventi Loaded in finestra, ad esempio:

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

Se è necessario associarlo in XAML, è necessario utilizzare ObjectDataProvider per creare l'oggetto disponibile come origine di associazione:

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

Attira l'attenzione sul prossimo codice:

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

Guida su come mappare lo spazio dei nomi e l'assembly che puoi leggere su MSDN .

property textbox

Sto cercando di trovare un semplice esempio in cui le enumerazioni sono mostrate così come sono. Tutti gli esempi che ho visto cercano di aggiungere stringhe di visualizzazione di aspetto piacevole, ma non voglio quella complessità.

Fondamentalmente ho una classe che contiene tutte le proprietà che leghiamo, impostando prima DataContext su questa classe e quindi specificando l'associazione come questa nel file xaml:

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

Ma questo non mostra i valori enum nel ComboBox come oggetti.




Ho usato un'altra soluzione usando MarkupExtension.

  1. Ho fatto lezione che fornisce elementi fonte:

    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. Questo è quasi tutto ... Ora usalo in XAML:

        <ComboBox DisplayMemberPath="DisplayName"
              ItemsSource="{persons:EnumToItemsSource {x:Type enums:States}}"
              SelectedValue="{Binding Path=WhereEverYouWant}"
              SelectedValuePath="Value" />
    
  3. Cambia 'enums: States' al tuo enum




Dovrai creare una matrice di valori nell'enumerazione, che può essere creata chiamando System.Enum.GetValues() , passandogli il Type di enumerazione di cui desideri disporre.

Se lo specifichi per la proprietà ItemsSource , allora dovrebbe essere popolato con tutti i valori dell'enumerazione. Probabilmente vuoi associare SelectedItem a EffectStyle (supponendo che sia una proprietà della stessa enumerazione e contenga il valore corrente).




Tutti i post di cui sopra hanno perso un semplice trucco. È possibile dal binding di SelectedValue scoprire come compilare ItemsSource AUTOMATICAMENTE in modo che il markup XAML sia corretto.

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

Ad esempio nel mio ViewModel che ho

public enum FoolEnum
    {
        AAA, BBB, CCC, DDD

    };


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

ValidateRaiseAndSetIfChanged è il mio hook INPC. I tuoi possono differire.

L'implementazione di EnumComboBox è la seguente, ma prima avrò bisogno di un piccolo aiuto per ottenere le mie stringhe e valori di enumerazione

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

e la classe principale (Nota Sto usando ReactiveUI per l'aggancio delle modifiche alle proprietà tramite WhenAny)

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

        }
    }
}

Devi anche impostare correttamente lo stile in Generic.XAML, altrimenti la tua scatola non renderà nulla e ti strapperai i capelli.

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

e questo è quello. Questo potrebbe ovviamente essere esteso per supportare i18n ma renderebbe il post più lungo.




Le app universali sembrano funzionare in modo un po 'diverso; non ha tutta la potenza del XAML completo. Quello che ha funzionato per me è:

  1. Ho creato un elenco dei valori enum come enumerazione (non convertito in stringhe o in interi) e ho associato l'oggetto Items di ComboBox a
  2. Quindi potrei associare il ComboBox ItemSelected alla mia proprietà pubblica il cui tipo è l'enum in questione

Solo per divertimento ho montato un piccolo modulo per aiutarlo e lo ho pubblicato sulle pagine MSDN Samples . I bit extra mi permettono di ignorare facoltativamente i nomi delle enumerazioni e di farmi nascondere alcune enumerazioni. Il mio codice sembra terribile come quello di Nick (sopra), che vorrei aver visto prima.




Mi è piaciuta la risposta di tom.maruska , ma avevo bisogno di supportare qualsiasi tipo di enum che il mio modello potrebbe incontrare in fase di runtime. Per questo, ho dovuto usare un'associazione per specificare il tipo dell'estensione di markup. Sono stato in grado di lavorare in questa risposta di nicolay.anykienko per trovare un'estensione di markup molto flessibile che avrebbe funzionato in ogni caso a cui possa pensare. Si consuma così:

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

La fonte dell'estensione di markup mashed di cui sopra:

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("_", " ");
    }
}



Sto aggiungendo il mio commento (in VB, purtroppo, ma il concetto può essere facilmente replicato su C # in un battito cardiaco), perché dovevo solo fare riferimento a questo e non mi piaceva nessuna delle risposte in quanto troppo complesse. Non dovrebbe essere così difficile.

Così ho trovato un modo più semplice. Associare gli enumeratori a un dizionario. Collega quel dizionario al Combobox.

La mia casella combinata:

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

Il mio code-behind. Speriamo che questo aiuti qualcun altro.

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



Related

c# .net wpf xaml data-binding