Bagaimana cara memiliki kotak kombo terikat enum dengan pemformatan string khusus untuk nilai enum?

136

Di posting Enum ToString , metode dijelaskan untuk menggunakan atribut khusus DescriptionAttributeseperti ini:

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

Dan kemudian, Anda memanggil suatu fungsi GetDescription, menggunakan sintaks seperti:

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

Tetapi itu tidak benar-benar membantu saya ketika saya hanya ingin mengisi ComboBox dengan nilai enum, karena saya tidak bisa memaksa ComboBox untuk memanggilGetDescription .

Yang saya inginkan memiliki persyaratan berikut:

  • Membaca (HowNice)myComboBox.selectedItemakan mengembalikan nilai yang dipilih sebagai nilai enum.
  • Pengguna harus melihat string tampilan yang mudah digunakan, dan bukan hanya nama nilai pencacahan. Jadi, alih-alih melihat " NotNice", pengguna akan melihat " Not Nice At All".
  • Mudah-mudahan, solusinya membutuhkan sedikit perubahan kode pada pencacahan yang ada.

Jelas, saya bisa mengimplementasikan kelas baru untuk setiap enum yang saya buat, dan menimpanya ToString(), tapi itu banyak pekerjaan untuk setiap enum, dan saya lebih suka menghindarinya.

Ada ide?

Heck, saya bahkan akan memeluk sebagai hadiah :-)

Shalom Craimer
sumber
1
jjnguy benar bahwa Java enum menyelesaikan ini dengan baik ( javahowto.blogspot.com/2006/10/… ), tapi itu relevansinya dipertanyakan.
Matthew Flaschen
8
Java Enums adalah lelucon. Mungkin mereka akan menambahkan Properti pada tahun 2020: /
Chad Grant
Untuk solusi yang lebih ringan (tapi bisa dibilang kurang kuat) lihat utas saya .
Gutblender

Jawaban:

42

Anda bisa menulis TypeConverter yang membaca atribut tertentu untuk mencarinya di resource Anda. Dengan demikian Anda akan mendapatkan dukungan multi-bahasa untuk nama tampilan tanpa banyak kerumitan.

Lihat ke dalam metode ConvertFrom / ConvertTo TypeConverter, dan gunakan refleksi untuk membaca atribut di bidang enum Anda .

sisve
sumber
Oke, saya menulis beberapa kode (lihat jawaban saya untuk pertanyaan ini) - apakah menurut Anda itu cukup, apakah saya melewatkan sesuatu?
Shalom Craimer
1
Bagus. Lebih baik secara keseluruhan, tetapi mungkin berlebihan untuk perangkat lunak run-of-the-mill Anda yang tidak akan pernah diglobalisasi-dalam-cara-apapun. (Saya tahu, asumsi itu akan berubah menjadi salah nanti. ;-))
peSHIr
85

ComboBoxmemiliki semua yang Anda butuhkan: FormattingEnabledproperti, yang harus Anda setel true, dan Formatevent, di mana Anda perlu menempatkan logika pemformatan yang diinginkan. Jadi,

myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
    {
        e.Value = GetDescription<HowNice>((HowNice)e.Value);
    }
Anton Gogolev
sumber
Apakah ini hanya bekerja dengan kotak kombo terikat data? Saya tidak bisa mengaktifkan acara Format jika tidak.
SomethingBetter
satu-satunya masalah di sini adalah Anda tidak dapat memiliki daftar yang diurutkan dengan logika Anda
GorillaApe
Ini adalah solusi yang bagus. Saya akan membutuhkannya untuk bekerja dengan a DataGridComboBoxColumn. Ada kesempatan untuk menyelesaikannya? Saya tidak bisa menemukan cara untuk mendapatkan akses ke ComboBoxsatu DataGridComboBoxColumn.
Soko
46

Jangan! Enum adalah primitif dan bukan objek UI - membuatnya melayani UI dalam .ToString () akan sangat buruk dari sudut pandang desain. Anda mencoba menyelesaikan masalah yang salah di sini: masalah sebenarnya adalah Anda tidak ingin Enum.ToString () muncul di kotak kombo!

Sekarang ini adalah masalah yang sangat bisa dipecahkan! Anda membuat objek UI untuk mewakili item kotak kombo Anda:

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

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

Dan kemudian tambahkan saja contoh kelas ini ke koleksi Item kotak kombo Anda dan setel properti ini:

comboBox.ValueMember = "Value";
comboBox.DisplayMember = "Description";
Sander
sumber
1
Saya setuju dengan sepenuh hati. Anda juga tidak boleh mengekspos hasil ToString () ke UI. Dan, Anda tidak mendapatkan pelokalan.
Øyvind Skaar
Saya tahu ini sudah tua, tapi apa bedanya?
nportelli
2
Saya telah melihat solusi serupa di mana alih-alih menggunakan kelas khusus, mereka memetakan nilai enum ke a Dictionarydan menggunakan properti Keydan Valuesebagai DisplayMemberdan ValueMember.
Jeff B
42

TypeConverter. Saya rasa inilah yang saya cari. Salam Simon Svensson !

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

Yang perlu saya ubah dalam enum saya saat ini adalah menambahkan baris ini sebelum deklarasi mereka.

[TypeConverter(typeof(EnumToStringUsingDescription))]

Setelah saya melakukannya, enum apa pun akan ditampilkan menggunakan DescriptionAttributebidangnya.

Oh, dan TypeConverterakan didefinisikan seperti ini:

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

Ini membantu saya dengan kasus ComboBox saya, tetapi jelas tidak benar-benar menimpa ToString(). Saya kira saya akan menerima ini sementara ...

Shalom Craimer
sumber
3
Anda menangani Enum -> String, tetapi Anda juga memerlukan Enum> InstanceDescriptor, dan String -> Enum jika Anda ingin implementasi yang lengkap. Tapi saya rasa menampilkannya sudah cukup untuk kebutuhan Anda saat ini. ;)
sisve
1
Sayangnya, solusi ini hanya berfungsi jika deskripsi Anda statis.
Llyle
Omong-omong, penggunaan TypeConverter tidak terikat pada deskripsi statis, coverter dapat mengisi nilai dari sumber selain atribut.
Dmitry Tashkinov
3
Telah menarik rambut saya selama beberapa jam sekarang, tetapi tampaknya masih tidak berfungsi bahkan di aplikasi konsol sederhana. Saya menghias enum dengan [TypeConverter(typeof(EnumToStringUsingDescription))] public enum MyEnum {[Description("Blah")] One}, mencoba melakukan Console.WriteLine(MyEnum.One)dan masih keluar sebagai "Satu". Apakah Anda memerlukan sihir khusus seperti TypeDescriptor.GetConverter(typeof(MyEnum)).ConvertToString(MyEnum.One)(yang berfungsi dengan baik)?
Dav
1
@scraimer Saya telah mem-posting versi dari bendera pendukung kode Anda. semua hak milik Anda ...
Avi Turner
33

Menggunakan contoh pencacahan Anda:

using System.ComponentModel;

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

Buat Ekstensi:

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

Kemudian Anda dapat menggunakan sesuatu seperti berikut:

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

Lihat: http://www.blackwasp.co.uk/EnumDescription.aspx untuk informasi lebih lanjut. Penghargaan diberikan kepada Richrd Carr untuk solusinya

Tyler Durden
sumber
Saya mengikuti detail di situs yang dirujuk dan menggunakannya sebagai berikut, tampak langsung bagi saya 'string myDesc = HowNice.ReallyNice.Description ();' myDesc akan menampilkan Really Nice
Ananda
8

Anda bisa membuat struct generik yang bisa Anda gunakan untuk semua enum yang memiliki deskripsi. Dengan konversi implisit ke dan dari kelas, variabel Anda masih berfungsi seperti enum kecuali untuk metode 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;
    }

}

Contoh penggunaan:

Described<HowNice> nice = HowNice.ReallyNice;

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

Saya tidak berpikir Anda dapat melakukannya tanpa hanya mengikat jenis yang berbeda - setidaknya, tidak nyaman. Biasanya, bahkan jika Anda tidak dapat mengontrol ToString(), Anda dapat menggunakan TypeConverteruntuk melakukan pemformatan khusus - tetapi IIRC System.ComponentModelbarang - barang tersebut tidak menghormati ini untuk enum.

Anda bisa mengikat ke salah string[]satu deskripsi, atau sesuatu yang pada dasarnya seperti pasangan kunci / nilai? (deskripsi / nilai) - sesuatu seperti:

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

Dan kemudian mengikat ke EnumWrapper<HowNice>.GetValues()

Marc Gravell
sumber
1
Nama 'GetDescription' tidak ada dalam konteks saat ini. im menggunakan .NET 4.0
Muhammad Adeel Zahid
@MuhammadAdeelZahid mencermati awal pertanyaan - yang berasal dari postingan tertaut: stackoverflow.com/questions/479410/enum-tostring
Marc Gravell
maaf tapi tidak bisa mendapatkan petunjuk dari pertanyaan itu. metode Anda tidak dikompilasi dan menunjukkan kesalahan.
Muhammad Adeel Zahid
Hai Marc, saya mencoba ide Anda. Ini berfungsi, tetapi theComboBox.SelectItemadalah tipe EnumWrapper<T>, bukan Tdirinya sendiri. Saya pikir scraimer ingin Reading (HowNice)myComboBox.selectedItem will return the selected value as the enum value..
Peter Lee
5

Cara terbaik untuk melakukannya adalah dengan membuat kelas.

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!");
}

Saya yakin itulah cara terbaik untuk melakukannya.

Ketika dimasukkan ke dalam kotak kombo, ToString cantik akan ditampilkan, dan fakta bahwa tidak ada lagi yang dapat membuat contoh kelas Anda lagi pada dasarnya menjadikannya enum.

ps mungkin perlu ada sedikit perbaikan sintaks, saya tidak terlalu baik dengan C #. (Orang Jawa)

jjnguy
sumber
1
Bagaimana ini membantu masalah kotak kombo?
peSHIr
Nah, sekarang, ketika objek baru dimasukkan ke dalam kotak kombo, ToStringnya akan ditampilkan dengan benar, dan kelas tersebut masih bertindak seperti enum.
jjnguy
1
Akan menjadi jawaban saya juga.
Mikko Rantanen
3
Dan melihat bagaimana poster asli secara eksplisit tidak menginginkan kelas. Menurutku kelas bukanlah pekerjaan yang lebih banyak. Anda bisa mengabstraksi deskripsi dan ToString menimpa ke kelas induk untuk semua enum. Setelah ini yang Anda butuhkan hanyalah konstruktor private HowNice(String desc) : base(desc) { }dan bidang statis.
Mikko Rantanen
Saya berharap untuk menghindari ini, karena itu berarti setiap enumerasi yang saya buat akan membutuhkan kelasnya sendiri. Ugh.
Shalom Craimer
3

Mengingat bahwa Anda lebih suka tidak membuat kelas untuk setiap enum, saya sarankan membuat kamus teks nilai / tampilan enum dan mengikatnya sebagai gantinya.

Perhatikan bahwa ini memiliki ketergantungan pada metode metode GetDescription di postingan asli.

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;
}
Richard Szalay
sumber
Ide bagus. Tapi bagaimana saya menggunakan ini dengan kotak kombo? Setelah pengguna memilih item dari kotak kombo, bagaimana saya tahu item mana yang dia pilih? Cari berdasarkan string Deskripsi? Itu membuat kulit saya gatal ... (mungkin ada "benturan" string antara string Deskripsi)
Shalom Craimer
Kunci item yang dipilih akan menjadi nilai enum sebenarnya. Selain itu, jangan bertabrakan dengan string deskripsi - bagaimana pengguna membedakannya?
Richard Szalay
<cont> jika Anda memiliki string deskripsi yang bertabrakan, maka Anda tidak boleh mengikat nilai enum ke kotak kombo secara langsung.
Richard Szalay
hmmm ... Baiklah, dapatkah Anda memberi saya contoh kode bagaimana Anda akan menambahkan item ke kotak kombo? Yang bisa saya pikirkan hanyalah "foreach (string s in descriptionDict.Values) {this.combobox.Items.Add (s);}"
Shalom Craimer
1
ComboBox.DataSource = kamus;
Richard Szalay
3

Tidak mungkin untuk menimpa ToString () dari enum di C #. Namun, Anda dapat menggunakan metode ekstensi;

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

Tentu saja Anda harus membuat panggilan eksplisit ke metode tersebut, yaitu;

HowNice.ReallyNice.ToString(0)

Ini bukan solusi yang bagus, dengan pernyataan sakelar dan semuanya - tetapi itu harus berfungsi dan mudah-mudahan memutihkan banyak penulisan ulang ...

Björn
sumber
Ketahuilah bahwa kontrol yang mengikat ke enum Anda tidak akan memanggil metode ekstensi ini, melainkan akan memanggil implementasi default.
Richard Szalay
Baik. Jadi ini adalah opsi yang layak jika Anda memerlukan deskripsi di suatu tempat, itu tidak membantu dengan masalah kotak kombo yang ditimbulkan.
peSHIr
Masalah yang lebih besar adalah ini tidak akan pernah dipanggil (sebagai metode ekstensi) - metode instance yang sudah ada selalu diprioritaskan.
Marc Gravell
Tentu saja Marc benar (seperti biasa?). Pengalaman NET saya minimal, tetapi memberikan parameter dummy ke metode harus melakukan triknya. Jawaban diedit.
Björn
2

Menindaklanjuti jawaban @scraimer, berikut adalah versi konverter tipe enum-ke-string, yang juga mendukung 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;
    }

}

Dan metode ekstensi untuk menggunakannya:

    /// <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;
    }
Avi Turner
sumber
1

Saya akan menulis kelas generik untuk digunakan dengan jenis apa pun. Saya pernah menggunakan sesuatu seperti ini di masa lalu:

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
}

Di atas ini, Anda dapat menambahkan "metode pabrik" statis untuk membuat daftar item kotak kombo yang diberi jenis enum (hampir sama dengan metode GetDescriptions yang Anda miliki di sana). Ini akan menyelamatkan Anda dari keharusan untuk mengimplementasikan satu entitas per setiap jenis enum, dan juga menyediakan tempat yang bagus / logis untuk metode pembantu "GetDescriptions" (secara pribadi saya akan menyebutnya FromEnum (T obj) ...

Dan C.
sumber
1

Buat koleksi yang berisi apa yang Anda butuhkan (seperti objek sederhana yang berisi Valueproperti yang berisi nilai HowNiceenum dan Descriptionproperti yang berisi GetDescription<HowNice>(Value)dan databind combo ke koleksi itu.

Sedikit seperti ini:

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

ketika Anda memiliki kelas koleksi seperti ini:

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

}

Seperti yang Anda lihat, koleksi ini mudah disesuaikan dengan lambda's untuk memilih subset dari enumerator Anda dan / atau menerapkan pemformatan kustom stringdaripada menggunakan GetDescription<T>(x)fungsi yang Anda sebutkan.

peSHIr
sumber
Luar biasa, tapi saya mencari sesuatu yang membutuhkan lebih sedikit pekerjaan dalam kode.
Shalom Craimer
Bahkan jika Anda dapat menggunakan koleksi umum yang sama untuk hal semacam ini untuk semua enumerator Anda? Saya tidak menyarankan penulisan kustom seperti koleksi untuk setiap enum, tentu saja.
peSHIr
1

Anda dapat menggunakan PostSharp untuk menargetkan Enum.ToString dan menambahkan semua kode yang Anda inginkan. Ini tidak memerlukan perubahan kode apa pun.

majkinetor
sumber
1

Yang Anda butuhkan adalah mengubah enum menjadi ReadonlyCollection dan mengikat koleksi ke kotak kombo (atau kontrol yang mengaktifkan Key-Value Pair dalam hal ini)

Pertama, Anda memerlukan kelas untuk memuat item dari daftar. Karena yang Anda butuhkan hanyalah pasangan int / string yang saya sarankan menggunakan antarmuka dan kombinasi kelas dasar sehingga Anda dapat menerapkan fungsionalitas di objek apa pun yang Anda inginkan:

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
}

Berikut adalah antarmuka dan contoh kelas yang mengimplementasikannya Perhatikan bahwa kelas 'Key diketik dengan kuat ke Enum, dan bahwa properti IValueDescritionItem diimplementasikan secara eksplisit (sehingga kelas dapat memiliki properti apa pun dan Anda dapat MEMILIH properti yang mengimplementasikan Pasangan Kunci / Nilai.

Sekarang kelas 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)];
        }
    }

}

Jadi yang Anda butuhkan dalam kode Anda adalah:

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

Ingat bahwa koleksi Anda diketik dengan MyItem sehingga nilai kotak kombo harus mengembalikan nilai enum jika Anda mengikat ke properti yang sesuai.

Saya menambahkan properti T this [Enum t] untuk membuat koleksi lebih berguna daripada combo yang dapat dikonsumsi, misalnya textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;

Anda tentu saja dapat memilih untuk mengubah MyItem menjadi kelas Key / Value yang digunakan hanya untuk puprose ini secara efektif melewatkan MyItem dalam argumen tipe EnumToReadnlyCollection sama sekali, tetapi kemudian Anda akan dipaksa untuk menggunakan int untuk kunci (artinya mendapatkan combobox1.SelectedValue akan mengembalikan int dan bukan tipe enum). Anda mengatasinya jika Anda membuat kelas KeyValueItem untuk menggantikan MyItem dan seterusnya dan seterusnya ...


sumber
1

Maaf untuk menyelesaikan utas lama ini.

Saya akan melakukan cara berikut untuk melokalkan enum, karena dapat menampilkan nilai yang bermakna dan dilokalkan kepada pengguna, bukan hanya deskripsi, melalui bidang teks daftar turun-bawah dalam contoh ini.

Pertama, saya membuat metode sederhana yang disebut OwToStringByCulture untuk mendapatkan string yang dilokalkan dari file sumber daya global, dalam contoh ini adalah BiBongNet.resx di folder App_GlobalResources. Di dalam file sumber daya ini, pastikan Anda memiliki semua string yang sama dengan nilai enum (ReallyNice, SortOfNice, NotNice). Dalam metode ini, saya meneruskan parameter: resourceClassName yang biasanya merupakan nama file resource.

Selanjutnya, saya membuat metode statis untuk mengisi dropdownlist dengan enum sebagai sumber datanya, yang disebut OwFillDataWithEnum. Metode ini dapat digunakan dengan enum apa pun nanti.

Kemudian di halaman dengan dropdownlist bernama DropDownList1, saya mengatur di Page_Load berikut hanya satu baris kode sederhana untuk mengisi enum ke dropdownlist.

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

Itu dia. Saya rasa dengan beberapa metode sederhana seperti ini, Anda dapat mengisi kontrol daftar apa pun dengan enum apa pun, tidak hanya sebagai nilai deskriptif tetapi juga teks yang dilokalkan untuk ditampilkan. Anda dapat menjadikan semua metode ini sebagai metode ekstensi untuk penggunaan yang lebih baik.

Semoga bantuan ini. Bagikan untuk dibagikan!

Berikut caranya:

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);
        }
}
BiBongNet
sumber
1
Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

Untuk mengatasi ini, Anda harus menggunakan Metode ekstensi dan Array of Strings seperti ini:

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];
 }
}

Kode sederhana dan decoding cepat.

Sérgio
sumber
variabel string harus Statis dan dideklarasikan seperti ini: String Statis [] strings = new [] {...}
Sérgio
Satu-satunya masalah dengan ini, adalah Anda perlu memiliki fungsi untuk setiap pencacahan, dan deskripsinya akan terpisah dari pencacahan itu sendiri ...
Avi Turner
1

Saya mencoba pendekatan ini dan berhasil untuk saya.

Saya membuat kelas pembungkus untuk enum dan membebani operator implisit sehingga saya dapat menetapkannya ke variabel enum (dalam kasus saya, saya harus mengikat objek ke ComboBoxnilai).

Anda dapat menggunakan refleksi untuk memformat nilai enum seperti yang Anda inginkan, dalam kasus saya, saya mengambil DisplayAttributekeluar dari nilai enum (jika ada).

Semoga ini membantu.

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

EDIT:

Untuk berjaga-jaga, saya menggunakan fungsi berikut untuk mendapatkan enumnilai yang saya gunakan untuk DataSourcedariComboBox

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;
    }
}
Ezequiel Moneró Thompson
sumber
0

Setelah Anda memiliki GetDescriptionmetode (harus statis global), Anda dapat menggunakan ini melalui metode ekstensi:

public static string ToString(this HowNice self)
{
    return GetDescription<HowNice>(self);
}
perasaan kagum
sumber
0
Enum HowNice {   
[StringValue("Really Nice")]   
ReallyNice,   
[StringValue("Kinda Nice")]   
SortOfNice,   
[StringValue("Not Nice At All")]   
NotNice 
}

Status = ReallyNice.GetDescription()
pengguna1308805
sumber
3
Selamat datang di stackoverflow! Itu selalu lebih baik untuk memberikan deskripsi singkat untuk kode sampel untuk meningkatkan akurasi posting :)
Picrofo Software
-1

Anda dapat mendefinisikan Enum sebagai

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

dan kemudian gunakan HowNice.GetStringValue().

Vrushal
sumber
2
Ini tidak dapat dikompilasi (saya punya .NET 3.5). Di mana ´StringValue´ dideklarasikan?
kagum
1
Jawaban dari @scraimer sama, kecuali bahwa dia menggunakan atribut di luar kerangka, sedangkan Anda menggunakan beberapa jenis atribut yang ditentukan sendiri.
Oliver