Come posso avere una casella combinata associata a enum con formattazione di stringa personalizzata per i valori di enum?


135

Nel post Enum ToString , viene descritto un metodo per utilizzare l'attributo personalizzato in DescriptionAttributequesto modo:

Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

E poi, chiami una funzione GetDescription, usando la sintassi come:

GetDescription<HowNice>(NotNice); // Returns "Not Nice At All"

Ma questo non mi aiuta davvero quando voglio semplicemente popolare un ComboBox con i valori di un enum, dal momento che non posso forzare il ComboBox a chiamareGetDescription .

Quello che voglio ha i seguenti requisiti:

  • La lettura (HowNice)myComboBox.selectedItemrestituirà il valore selezionato come valore enum.
  • L'utente dovrebbe vedere le stringhe di visualizzazione intuitive e non solo il nome dei valori di enumerazione. Quindi, invece di vedere " NotNice", l'utente vedrebbe " Not Nice At All".
  • Si spera che la soluzione richieda modifiche minime del codice alle enumerazioni esistenti.

Ovviamente, potrei implementare una nuova classe per ogni enum che creo e sovrascriverla ToString(), ma è un sacco di lavoro per ogni enum e preferirei evitarlo.

Qualche idea?

Cavolo, lancerò anche un abbraccio come una taglia :-)


1
jjnguy ha ragione sul fatto che gli enum di Java lo risolvono bene ( javahowto.blogspot.com/2006/10/… ), ma questo è di dubbia rilevanza.
Matthew Flaschen,

8
Java Enums è uno scherzo. Forse aggiungeranno Proprietà nel 2020: /
Chad Grant

Per una soluzione più leggera (ma probabilmente meno robusta) vedi il mio thread .
Gutblender,

Risposte:


42

È possibile scrivere un TypeConverter che legge gli attributi specificati per cercarli nelle risorse. In questo modo otterrai supporto multilingue per i nomi visualizzati senza troppi problemi.

Esamina i metodi ConvertFrom / ConvertTo di TypeConverter e usa la riflessione per leggere gli attributi nei campi enum .


OK, ho scritto un po 'di codice (vedi la mia risposta a questa domanda): pensi che sia abbastanza, mi sto perdendo qualcosa?
Shalom Craimer,

1
Ben fatto. Meglio esagerare, ma potrebbe essere eccessivo per il tuo software di routine che non sarà mai globalizzato in alcun modo. (So ​​che
quell'assunto si

85

ComboBoxha tutto ciò di cui hai bisogno: la FormattingEnabledproprietà, che dovresti impostare true, e l' Formatevento, dove dovrai posizionare la logica di formattazione desiderata. Così,

myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
    {
        e.Value = GetDescription<HowNice>((HowNice)e.Value);
    }

Funziona solo con combobox databound? In caso contrario, non è possibile attivare l'evento Format.
Qualcosa di meglio il

l'unico problema qui è che non puoi avere un elenco ordinato con la tua logica
GorillaApe

Questa è un'ottima soluzione Ne avrei bisogno per funzionare con un DataGridComboBoxColumnpensiero. Qualche possibilità di risolverlo? Io non sono in grado di trovare un modo per ottenere l'accesso alla ComboBoxdel DataGridComboBoxColumn.
Soko,

46

Non farlo! Gli enum sono primitivi e non oggetti dell'interfaccia utente: farli servire l'interfaccia utente in .ToString () sarebbe piuttosto male dal punto di vista del design. Stai cercando di risolvere il problema sbagliato qui: il vero problema è che non vuoi che Enum.ToString () appaia nella casella combinata!

Ora questo è davvero un problema risolvibile! Si crea un oggetto UI per rappresentare gli elementi della casella combinata:

sealed class NicenessComboBoxItem
{
    public string Description { get { return ...; } }
    public HowNice Value { get; private set; }

    public NicenessComboBoxItem(HowNice howNice) { Value = howNice; }
}

Quindi aggiungi solo istanze di questa classe alla raccolta Elementi della casella combinata e imposta queste proprietà:

comboBox.ValueMember = "Value";
comboBox.DisplayMember = "Description";

1
Sono d'accordo con tutto il cuore. Né dovresti esporre il risultato di ToString () all'interfaccia utente. E non ottieni localizzazione.
Øyvind Skaar,

So che questo è vecchio, ma come è diverso?
nportelli,

2
Ho visto una soluzione simile in cui invece di utilizzare una classe personalizzata, hanno mappato i valori enum su a Dictionarye hanno usato le proprietà Keye Valuecome DisplayMembere ValueMember.
Jeff B,

42

TypeConverter. Penso che sia quello che stavo cercando. Tutti grandine Simon Svensson !

[TypeConverter(typeof(EnumToStringUsingDescription))]
Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

Tutto quello che devo cambiare nel mio enum attuale è aggiungere questa riga prima della loro dichiarazione.

[TypeConverter(typeof(EnumToStringUsingDescription))]

Una volta che lo faccio, qualsiasi enum verrà visualizzato usando i DescriptionAttributesuoi campi.

Oh, e il TypeConvertersarebbe definito in questo modo:

public class EnumToStringUsingDescription : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return (sourceType.Equals(typeof(Enum)));
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType.Equals(typeof(String)));
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (!destinationType.Equals(typeof(String)))
        {
            throw new ArgumentException("Can only convert to string.", "destinationType");
        }

        if (!value.GetType().BaseType.Equals(typeof(Enum)))
        {
            throw new ArgumentException("Can only convert an instance of enum.", "value");
        }

        string name = value.ToString();
        object[] attrs = 
            value.GetType().GetField(name).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
    }
}

Questo mi aiuta con la mia custodia ComboBox, ma ovviamente non ha la precedenza su ToString(). Immagino che mi accontenterò di questo nel frattempo ...


3
Stai gestendo Enum -> String, ma avrai anche bisogno di Enum> InstanceDescriptor e String -> Enum se vuoi un'implementazione completa. Ma suppongo che visualizzarlo sia sufficiente per le tue esigenze al momento. ;)
sisve,

1
Questa soluzione funziona solo quando le tue descrizioni sono statiche, sfortunatamente.
Llyle,

A proposito, l'uso di TypeConverter non è legato a descrizioni statiche, il coverter può popolare valori da fonti diverse dagli attributi.
Dmitry Tashkinov,

3
Mi sto strappando i capelli da qualche ora, ma non sembra funzionare nemmeno in semplici app per console. Ho decorato l'enum con [TypeConverter(typeof(EnumToStringUsingDescription))] public enum MyEnum {[Description("Blah")] One}, ho provato a farlo Console.WriteLine(MyEnum.One)e viene ancora visualizzato come "Uno". Hai bisogno di qualche magia speciale come TypeDescriptor.GetConverter(typeof(MyEnum)).ConvertToString(MyEnum.One)(che funziona benissimo)?
Dav,

1
@scraimer Ho pubblicato una versione del tuo codice che supporta flag. tutti i diritti riservati a te ...
Avi Turner

32

Utilizzando l'esempio di enumerazione:

using System.ComponentModel;

Enum HowNice
{
    [Description("Really Nice")]
    ReallyNice,
    [Description("Kinda Nice")]
    SortOfNice,
    [Description("Not Nice At All")]
    NotNice
}

Crea un'estensione:

public static class EnumExtensions
{
    public static string Description(this Enum value)
    {
        var enumType = value.GetType();
        var field = enumType.GetField(value.ToString());
        var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute),
                                                   false);
        return attributes.Length == 0
            ? value.ToString()
            : ((DescriptionAttribute)attributes[0]).Description;
    }
}

Quindi è possibile utilizzare qualcosa di simile al seguente:

HowNice myEnum = HowNice.ReallyNice;
string myDesc = myEnum.Description();

Vedere: http://www.blackwasp.co.uk/EnumDescription.aspx per ulteriori informazioni. Il merito va a Richrd Carr per la soluzione


Ho seguito i dettagli nel sito di riferimento e l'ho usato come segue, mi sembra semplice 'string myDesc = HowNice.ReallyNice.Description ();' myDesc uscirà Really Nice
Ananda il

8

Potresti creare una struttura generica che potresti usare per tutti i tuoi enum che hanno descrizioni. Con conversioni implicite da e verso la classe, le tue variabili funzionano ancora come l'enum ad eccezione del metodo ToString:

public struct Described<T> where T : struct {

    private T _value;

    public Described(T value) {
        _value = value;
    }

    public override string ToString() {
        string text = _value.ToString();
        object[] attr =
            typeof(T).GetField(text)
            .GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attr.Length == 1) {
            text = ((DescriptionAttribute)attr[0]).Description;
        }
        return text;
    }

    public static implicit operator Described<T>(T value) {
        return new Described<T>(value);
    }

    public static implicit operator T(Described<T> value) {
        return value._value;
    }

}

Esempio di utilizzo:

Described<HowNice> nice = HowNice.ReallyNice;

Console.WriteLine(nice == HowNice.ReallyNice); // writes "True"
Console.WriteLine(nice); // writes "Really Nice"

5

Non penso che tu possa farlo senza semplicemente associarti a un tipo diverso - almeno, non convenientemente. Normalmente, anche se non puoi controllarlo ToString(), puoi usare a TypeConverterper eseguire la formattazione personalizzata - ma IIRC ilSystem.ComponentModel materiale non lo rispetta per gli enum.

Potresti legarti a una string[]delle descrizioni, o qualcosa di essenzialmente simile a una coppia chiave / valore? (descrizione / valore) - qualcosa del tipo:

class EnumWrapper<T> where T : struct
{
    private readonly T value;
    public T Value { get { return value; } }
    public EnumWrapper(T value) { this.value = value; }
    public string Description { get { return GetDescription<T>(value); } }
    public override string ToString() { return Description; }

    public static EnumWrapper<T>[] GetValues()
    {
        T[] vals = (T[])Enum.GetValues(typeof(T));
        return Array.ConvertAll(vals, v => new EnumWrapper<T>(v));
    }
}

E poi si legano a EnumWrapper<HowNice>.GetValues()


1
Il nome "GetDescription" non esiste nel contesto corrente. sto usando .NET 4.0
Muhammad Adeel Zahid

@MuhammadAdeelZahid guardare da vicino l'inizio della questione - che deriva dal post collegato: stackoverflow.com/questions/479410/enum-tostring
Marc Gravell

scusa ma non riesco a ottenere alcun indizio dalla domanda. il tuo metodo non si sta compilando e mostra l'errore.
Muhammad Adeel Zahid

Ciao Marc, ho provato la tua idea. Funziona, ma theComboBox.SelectItemè il tipo di EnumWrapper<T>, anziché Tse stesso. Penso che lo scraimer voglia Reading (HowNice)myComboBox.selectedItem will return the selected value as the enum value..
Peter Lee,

5

Il modo migliore per farlo è fare una lezione.

class EnumWithToString {
    private string description;
    internal EnumWithToString(string desc){
        description = desc;
    }
    public override string ToString(){
        return description;
    }
}

class HowNice : EnumWithToString {

    private HowNice(string desc) : base(desc){}

    public static readonly HowNice ReallyNice = new HowNice("Really Nice");
    public static readonly HowNice KindaNice = new HowNice("Kinda Nice");
    public static readonly HowNice NotVeryNice = new HowNice("Really Mean!");
}

Credo che sia il modo migliore per farlo.

Se inserito in combobox, verrà mostrato il grazioso ToString e il fatto che nessuno possa creare più istanze della tua classe lo rende essenzialmente un enum.

ps potrebbe esserci bisogno di alcune lievi correzioni di sintassi, non sono molto bravo con C #. (Ragazzo Java)


1
In che modo aiuta con il problema della casella combinata?
peSHIr

Bene, ora, quando il nuovo oggetto viene messo in una casella combinata, il suo ToString verrà visualizzato correttamente e la classe agirà comunque come un enum.
jjnguy,

1
Sarebbe stata anche la mia risposta.
Mikko Rantanen,

3
E vedendo come il poster originale non volesse esplicitamente una lezione. Non credo che una lezione sia molto più lavoro. È possibile sottrarre la descrizione e ToString sovrascrivere a una classe genitore per tutti gli enum. Dopo tutto ciò che serve è un costruttore private HowNice(String desc) : base(desc) { }e i campi statici.
Mikko Rantanen,

Speravo di evitarlo, poiché significa che ogni enumerazione che faccio richiederà la propria classe. Ugh.
Shalom Craimer,

3

Dato che preferisci non creare una classe per ogni enum, ti consiglio di creare un dizionario del valore enum / visualizzare il testo e legarlo invece.

Si noti che questo ha una dipendenza dai metodi del metodo GetDescription nel post originale.

public static IDictionary<T, string> GetDescriptions<T>()
    where T : struct
{
    IDictionary<T, string> values = new Dictionary<T, string>();

    Type type = enumerationValue.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be of Enum type", "enumerationValue");
    }

    //Tries to find a DescriptionAttribute for a potential friendly name
    //for the enum
    foreach (T value in Enum.GetValues(typeof(T)))
    {
        string text = value.GetDescription();

        values.Add(value, text);
    }

    return values;
}

Bella idea Ma come lo userei con una casella combinata? Una volta che l'utente seleziona un elemento dalla casella combinata, come faccio a sapere quale degli elementi ha selezionato? Cerca per stringa di descrizione? Questo mi fa prudere la pelle ... (potrebbe esserci una "collisione" di stringhe tra le stringhe di descrizione)
Shalom Craimer,

La chiave dell'elemento selezionato sarà il valore enum effettivo. Inoltre, non scontrare le stringhe di descrizione: come farà l'utente a distinguere?
Richard Szalay,

<cont> se hai stringhe di descrizione che si scontrano, non dovresti comunque associare i valori dell'enum direttamente a una casella combinata.
Richard Szalay,

hmmm ... Beh, potresti darmi un esempio di codice su come aggiungere elementi alla casella combinata? Tutto quello a cui riesco a pensare è "foreach (stringhe nelle descrizioniDict.Values) {this.combobox.Items.Add (s);}"
Shalom Craimer,

1
ComboBox.DataSource = dizionario;
Richard Szalay,

3

Impossibile sovrascrivere ToString () di enumerazioni in C #. Tuttavia, è possibile utilizzare metodi di estensione;

public static string ToString(this HowNice self, int neverUsed)
{
    switch (self)
    {
        case HowNice.ReallyNice:
            return "Rilly, rilly nice";
            break;
    ...

Ovviamente dovrai fare una chiamata esplicita al metodo, ovvero;

HowNice.ReallyNice.ToString(0)

Questa non è una buona soluzione, con un'istruzione switch e tutto il resto, ma dovrebbe funzionare e, si spera, senza molte riscritture ...


Tieni presente che il controllo che si lega al tuo enum non chiamerebbe questo metodo di estensione, chiamerebbe l'implementazione predefinita.
Richard Szalay,

Destra. Quindi questa è un'opzione praticabile se hai bisogno di una descrizione da qualche parte, non aiuta con il problema della casella combinata posta.
peSHIr

Un problema maggiore è che questo non verrà mai chiamato (come metodo di estensione): i metodi di istanza già esistenti hanno sempre la priorità.
Marc Gravell

Ovviamente Marc ha ragione (come sempre?). La mia esperienza .NET è minima, ma fornire un parametro fittizio al metodo dovrebbe fare il trucco. Risposta modificata.
Björn,

2

In seguito alla risposta di @scraimer, ecco una versione del convertitore di tipo enum-to-string, che supporta anche i flag:

    /// <summary>
/// A drop-in converter that returns the strings from 
/// <see cref="System.ComponentModel.DescriptionAttribute"/>
/// of items in an enumaration when they are converted to a string,
/// like in ToString().
/// </summary>
public class EnumToStringUsingDescription : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return (sourceType.Equals(typeof(Enum)));
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType.Equals(typeof(String)));
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType.Equals(typeof(String)))
        {
            string name = value.ToString();
            Type effectiveType = value.GetType();          

            if (name != null)
            {
                FieldInfo fi = effectiveType.GetField(name);
                if (fi != null)
                {
                    object[] attrs =
                    fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
                }

            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    /// <summary>
    /// Coverts an Enums to string by it's description. falls back to ToString.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    public string EnumToString(Enum value)
    {
        //getting the actual values
        List<Enum> values = EnumToStringUsingDescription.GetFlaggedValues(value);
        //values.ToString();
        //Will hold results for each value
        List<string> results = new List<string>();
        //getting the representing strings
        foreach (Enum currValue in values)
        {
            string currresult = this.ConvertTo(null, null, currValue, typeof(String)).ToString();;
            results.Add(currresult);
        }

        return String.Join("\n",results);

    }

    /// <summary>
    /// All of the values of enumeration that are represented by specified value.
    /// If it is not a flag, the value will be the only value retured
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    private static List<Enum> GetFlaggedValues(Enum value)
    {
        //checking if this string is a flaged Enum
        Type enumType = value.GetType();
        object[] attributes = enumType.GetCustomAttributes(true);
        bool hasFlags = false;
        foreach (object currAttibute in attributes)
        {
            if (enumType.GetCustomAttributes(true)[0] is System.FlagsAttribute)
            {
                hasFlags = true;
                break;
            }
        }
        //If it is a flag, add all fllaged values
        List<Enum> values = new List<Enum>();
        if (hasFlags)
        {
            Array allValues = Enum.GetValues(enumType);
            foreach (Enum currValue in allValues)
            {
                if (value.HasFlag(currValue))
                {
                    values.Add(currValue);
                }
            }



        }
        else//if not just add current value
        {
            values.Add(value);
        }
        return values;
    }

}

E un metodo di estensione per usarlo:

    /// <summary>
    /// Converts an Enum to string by it's description. falls back to ToString
    /// </summary>
    /// <param name="enumVal">The enum val.</param>
    /// <returns></returns>
    public static string ToStringByDescription(this Enum enumVal)
    {
        EnumToStringUsingDescription inter = new EnumToStringUsingDescription();
        string str = inter.EnumToString(enumVal);
        return str;
    }

1

Scriverei una classe generica da utilizzare con qualsiasi tipo. Ho usato qualcosa di simile in passato:

public class ComboBoxItem<T>
{
    /// The text to display.
    private string text = "";
    /// The associated tag.
    private T tag = default(T);

    public string Text
    {
        get
        {
            return text;
        }
    }

    public T Tag
    {
        get
        {
            return tag;
        }
    }

    public override string ToString()
    {
        return text;
    }

    // Add various constructors here to fit your needs
}

Inoltre, è possibile aggiungere un "metodo factory" statico per creare un elenco di elementi combinati in base a un tipo enum (praticamente uguale al metodo GetDescriptions disponibile). Questo ti risparmierebbe la necessità di implementare un'entità per ogni tipo di enum, e fornirebbe anche un posto logico per il metodo helper "GetDescriptions" (personalmente lo chiamerei FromEnum (T obj) ...


1

Crea una raccolta che contenga ciò di cui hai bisogno (come semplici oggetti contenenti una Valueproprietà contenente il HowNicevalore enum e una Descriptionproprietà che contiene GetDescription<HowNice>(Value)e associa la combinazione di dati a quella raccolta.

Un po 'come questo:

Combo.DataSource = new EnumeratedValueCollection<HowNice>();
Combo.ValueMember = "Value";
Combo.DisplayMember = "Description";

quando hai una classe di raccolta come questa:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Whatever.Tickles.Your.Fancy
{
    public class EnumeratedValueCollection<T> : ReadOnlyCollection<EnumeratedValue<T>>
    {
        public EnumeratedValueCollection()
            : base(ListConstructor()) { }
        public EnumeratedValueCollection(Func<T, bool> selection)
            : base(ListConstructor(selection)) { }
        public EnumeratedValueCollection(Func<T, string> format)
            : base(ListConstructor(format)) { }
        public EnumeratedValueCollection(Func<T, bool> selection, Func<T, string> format)
            : base(ListConstructor(selection, format)) { }
        internal EnumeratedValueCollection(IList<EnumeratedValue<T>> data)
            : base(data) { }

        internal static List<EnumeratedValue<T>> ListConstructor()
        {
            return ListConstructor(null, null);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, string> format)
        {
            return ListConstructor(null, format);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection)
        {
            return ListConstructor(selection, null);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection, Func<T, string> format)
        {
            if (null == selection) selection = (x => true);
            if (null == format) format = (x => GetDescription<T>(x));
            var result = new List<EnumeratedValue<T>>();
            foreach (T value in System.Enum.GetValues(typeof(T)))
            {
                if (selection(value))
                {
                    string description = format(value);
                    result.Add(new EnumeratedValue<T>(value, description));
                }
            }
            return result;
        }

        public bool Contains(T value)
        {
            return (Items.FirstOrDefault(item => item.Value.Equals(value)) != null);
        }

        public EnumeratedValue<T> this[T value]
        {
            get
            {
                return Items.First(item => item.Value.Equals(value));
            }
        }

        public string Describe(T value)
        {
            return this[value].Description;
        }
    }

    [System.Diagnostics.DebuggerDisplay("{Value} ({Description})")]
    public class EnumeratedValue<T>
    {
        private T value;
        private string description;
        internal EnumeratedValue(T value, string description) {
            this.value = value;
            this.description = description;
        }
        public T Value { get { return this.value; } }
        public string Description { get { return this.description; } }
    }

}

Come puoi vedere, questa raccolta è facilmente personalizzabile con lambda per selezionare un sottoinsieme del tuo enumeratore e / o implementare una formattazione personalizzata stringinvece di utilizzare la GetDescription<T>(x)funzione menzionata.


Eccellente, ma sto cercando qualcosa che richiede ancora meno lavoro nel codice.
Shalom Craimer,

Anche se puoi usare la stessa raccolta generica per questo genere di cose per tutti i tuoi enumeratori? Naturalmente non stavo suggerendo di scrivere una collezione simile per ogni enum.
peSHIr

1

È possibile utilizzare PostSharp come target Enum.ToString e aggiungere il codice di annuncio desiderato. Questo non richiede alcuna modifica del codice.


1

Ciò di cui hai bisogno è trasformare un enum in ReadonlyCollection e associare la raccolta alla casella combinata (o qualsiasi controllo abilitato per coppia chiave-valore per quella materia)

Prima di tutto hai bisogno di una classe per contenere gli elementi dell'elenco. Poiché tutto ciò che serve è la coppia int / string, suggerisco di utilizzare un'interfaccia e una combinazione di classe base in modo da poter implementare la funzionalità in qualsiasi oggetto desiderato:

public interface IValueDescritionItem
{
    int Value { get; set;}
    string Description { get; set;}
}

public class MyItem : IValueDescritionItem
{
    HowNice _howNice;
    string _description;

    public MyItem()
    {

    }

    public MyItem(HowNice howNice, string howNice_descr)
    {
        _howNice = howNice;
        _description = howNice_descr;
    }

    public HowNice Niceness { get { return _howNice; } }
    public String NicenessDescription { get { return _description; } }


    #region IValueDescritionItem Members

    int IValueDescritionItem.Value
    {
        get { return (int)_howNice; }
        set { _howNice = (HowNice)value; }
    }

    string IValueDescritionItem.Description
    {
        get { return _description; }
        set { _description = value; }
    }

    #endregion
}

Ecco l'interfaccia e una classe di esempio che la implementa. Nota che la classe 'Key è fortemente tipizzata in Enum e che le proprietà IValueDescritionItem sono implementate esplicitamente (quindi la classe può avere qualunque proprietà e puoi SCEGLI quelle che implementano il Coppia chiave / valore.

Ora la classe EnumToReadOnlyCollection:

public class EnumToReadOnlyCollection<T,TEnum> : ReadOnlyCollection<T> where T: IValueDescritionItem,new() where TEnum : struct
{
    Type _type;

    public EnumToReadOnlyCollection() : base(new List<T>())
    {
        _type = typeof(TEnum);
        if (_type.IsEnum)
        {
            FieldInfo[] fields = _type.GetFields();

            foreach (FieldInfo enum_item in fields)
            {
                if (!enum_item.IsSpecialName)
                {
                    T item = new T();
                    item.Value = (int)enum_item.GetValue(null);
                    item.Description = ((ItemDescription)enum_item.GetCustomAttributes(false)[0]).Description;
                    //above line should be replaced with proper code that gets the description attribute
                    Items.Add(item);
                }
            }
        }
        else
            throw new Exception("Only enum types are supported.");
    }

    public T this[TEnum key]
    {
        get 
        {
            return Items[Convert.ToInt32(key)];
        }
    }

}

Quindi tutto ciò di cui hai bisogno nel tuo codice è:

private EnumToReadOnlyCollection<MyItem, HowNice> enumcol;
enumcol = new EnumToReadOnlyCollection<MyItem, HowNice>();
comboBox1.ValueMember = "Niceness";
comboBox1.DisplayMember = "NicenessDescription";
comboBox1.DataSource = enumcol;

Ricorda che la tua raccolta è digitata con MyItem, quindi il valore della casella combinata dovrebbe restituire un valore enum se ti associ alla proprietà appropriata.

Ho aggiunto la proprietà T this [Enum t] per rendere la raccolta ancora più utile di un semplice consumabile combo, ad esempio textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;

Ovviamente puoi scegliere di trasformare MyItem in una classe Key / Value utilizzata solo per questo puprose saltando efficacemente MyItem negli argomenti di tipo di EnumToReadnlyCollection del tutto, ma poi saresti costretto ad andare con int per la chiave (che significa ottenere combobox1.SelectedValue restituire int e non il tipo enum). Si aggira il problema se si crea una classe KeyValueItem per sostituire MyItem e così via e così via ...


1

Ci scusiamo per aver risolto questo vecchio thread.

Vorrei andare nel modo seguente per localizzare enum, in quanto può mostrare all'utente valori significativi e localizzati, non solo una descrizione, attraverso un campo di testo a discesa in questo esempio.

Innanzitutto, creo un metodo semplice chiamato OwToStringByCulture per ottenere stringhe localizzate da un file di risorse globali, in questo esempio è BiBongNet.resx nella cartella App_GlobalResources. All'interno di questo file di risorse, assicurarsi di avere tutte le stringhe uguali ai valori dell'enum (ReallyNice, SortOfNice, NotNice). In questo metodo, passo il parametro: resourceClassName che di solito è il nome del file di risorse.

Successivamente, creo un metodo statico per riempire un elenco a discesa con enum come origine dati, chiamato OwFillDataWithEnum. Questo metodo può essere utilizzato in seguito con qualsiasi enum.

Quindi nella pagina con un elenco a discesa chiamato DropDownList1, ho impostato in Page_Load la seguente solo una semplice riga di codice per riempire l'enum di elenco a discesa.

 BiBongNet.OwFillDataWithEnum<HowNice>(DropDownList1, "BiBongNet");

Questo è tutto. Penso che con alcuni semplici metodi come questi, puoi riempire qualsiasi controllo elenco con qualsiasi enum, con non solo valori descrittivi ma testo localizzato da visualizzare. Puoi utilizzare tutti questi metodi come metodi di estensione per un migliore utilizzo.

Spero che questo aiuto. Condividi per essere condiviso!

Ecco i metodi:

public class BiBongNet
{

        enum HowNice
        {
            ReallyNice,
            SortOfNice,
            NotNice
        }

        /// <summary>
        /// This method is for filling a listcontrol,
        /// such as dropdownlist, listbox... 
        /// with an enum as the datasource.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ctrl"></param>
        /// <param name="resourceClassName"></param>
        public static void OwFillDataWithEnum<T>(ListControl ctrl, string resourceClassName)
        {
            var owType = typeof(T);
            var values = Enum.GetValues(owType);
            for (var i = 0; i < values.Length; i++)
            {
                //Localize this for displaying listcontrol's text field.
                var text = OwToStringByCulture(resourceClassName, Enum.Parse(owType, values.GetValue(i).ToString()).ToString());
                //This is for listcontrol's value field
                var key = (Enum.Parse(owType, values.GetValue(i).ToString()));
                //add values of enum to listcontrol.
                ctrl.Items.Add(new ListItem(text, key.ToString()));
            }
        }

        /// <summary>
        /// Get localized strings.
        /// </summary>
        /// <param name="resourceClassName"></param>
        /// <param name="resourceKey"></param>
        /// <returns></returns>
        public static string OwToStringByCulture(string resourceClassName, string resourceKey)
        {
                return (string)HttpContext.GetGlobalResourceObject(resourceClassName, resourceKey);
        }
}

1
Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

Per risolvere questo problema, è necessario utilizzare un metodo di estensione e una matrice di stringhe in questo modo:

Enum HowNice {
  ReallyNice  = 0,
  SortOfNice  = 1,
  NotNice     = 2
}

internal static class HowNiceIsThis
{
 const String[] strings = { "Really Nice", "Kinda Nice", "Not Nice At All" }

 public static String DecodeToString(this HowNice howNice)
 {
   return strings[(int)howNice];
 }
}

Codice semplice e decodifica rapida.


la variabile stringhe dovrebbe essere statica e dichiarata così: Static String [] stringhe = new [] {...}
Sérgio

L'unico problema è che dovrai avere una funzione per ogni enumerazione e la descrizione sarà parte dell'enumerazione stessa ...
Avi Turner,

1

Ho provato questo approccio e ha funzionato per me.

Ho creato una classe wrapper per enum e ho sovraccaricato l'operatore implicito in modo da poterlo assegnare alle variabili enum (nel mio caso ho dovuto associare un oggetto a un ComboBoxvalore).

È possibile utilizzare la riflessione per formattare i valori di enum nel modo desiderato, nel mio caso recupero i DisplayAttributevalori di enum (se esistenti).

Spero che questo ti aiuti.

public sealed class EnumItem<T>
{
    T value;

    public override string ToString()
    {
        return Display;
    }

    public string Display { get; private set; }
    public T Value { get; set; }

    public EnumItem(T val)
    {
        value = val;
        Type en = val.GetType();
        MemberInfo res = en.GetMember(val.ToString())?.FirstOrDefault();
        DisplayAttribute display = res.GetCustomAttribute<DisplayAttribute>();
        Display = display != null ? String.Format(display.Name, val) : val.ToString();
    }

    public static implicit operator T(EnumItem<T> val)
    {
        return val.Value;
    }

    public static implicit operator EnumItem<T>(T val)
    {
        return new EnumItem<T>(val);
    }
}

MODIFICARE:

Per ogni evenienza, utilizzo la seguente funzione per ottenere i enumvalori che utilizzo per DataSourceilComboBox

public static class Utils
{
    public static IEnumerable<EnumItem<T>> GetEnumValues<T>()
    {
        List<EnumItem<T>> result = new List<EnumItem<T>>();
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            result.Add(item);
        }
        return result;
    }
}

0

Una volta che hai il GetDescriptionmetodo (deve essere statico globale), puoi usarlo attraverso un metodo di estensione:

public static string ToString(this HowNice self)
{
    return GetDescription<HowNice>(self);
}

0
Enum HowNice {   
[StringValue("Really Nice")]   
ReallyNice,   
[StringValue("Kinda Nice")]   
SortOfNice,   
[StringValue("Not Nice At All")]   
NotNice 
}

Status = ReallyNice.GetDescription()

3
Benvenuto in StackOverflow! È sempre meglio fornire una breve descrizione di un codice di esempio per migliorare l'accuratezza dei post :)
Software Picrofo

-1

È possibile definire Enum come

Enum HowNice {   
[StringValue("Really Nice")]   
ReallyNice,   
[StringValue("Kinda Nice")]   
SortOfNice,   
[StringValue("Not Nice At All")]   
NotNice 
} 

e quindi usare HowNice.GetStringValue().


2
Questo non viene compilato (ho .NET 3.5). Dove viene dichiarato "StringValue"?
timore del

1
La risposta di @scraimer è la stessa, tranne per il fatto che sta usando un attributo fuori dal framework, mentre tu usi un qualche tipo di attributo auto-definito.
Oliver,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.