Enum değerleri için özel dize biçimlendirmesine sahip bir numaralandırmaya bağlı birleşik giriş kutusuna nasıl sahip olabilirim?


135

Enum ToString yazısında , özel özniteliği şu şekilde kullanmak için bir yöntem açıklanmıştır DescriptionAttribute:

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

Ve sonra, aşağıdaki GetDescriptiongibi sözdizimini kullanarak bir işlevi çağırırsınız :

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

Ancak , ComboBox'ı aramaya zorlayamadığımdan, bir ComboBox'ı bir enum değerleriyle doldurmak istediğimde buGetDescription bana gerçekten yardımcı olmuyor .

İstediğim şey aşağıdaki gereksinimlere sahip:

  • Okuma (HowNice)myComboBox.selectedItem, seçilen değeri enum değeri olarak döndürecektir.
  • Kullanıcı, yalnızca numaralandırma değerlerinin adını değil, kullanıcı dostu ekran dizelerini de görmelidir. Dolayısıyla NotNicekullanıcı " " görmek yerine " " görürdü Not Nice At All.
  • Umarım çözüm, mevcut numaralandırmalarda minimum kod değişikliği gerektirecektir.

Açıkçası, oluşturduğum her bir enum için yeni bir sınıf uygulayabilirim ve onu geçersiz kılabilirim ToString(), ancak bu her bir enum için çok fazla iş ve bundan kaçınmayı tercih ederim.

Herhangi bir fikir?

Heck, bir ödül olarak kucaklaşacağım bile :-)


1
jjnguy, Java numaralandırmalarının bunu güzelce çözdüğü konusunda haklı ( javahowto.blogspot.com/2006/10/… ), ancak bunun alaka düzeyi tartışmalı.
Matthew Flaschen

8
Java Enums bir şakadır. Belki 2020'de Mülk ekleyecekler: /
Chad Grant

Daha hafif (ama muhtemelen daha az sağlam) çözümü için bkz benim iplik .
Gutblender

Yanıtlar:


42

Kaynaklarınızda aramak için belirtilen öznitelikleri okuyan bir TypeConverter yazabilirsiniz. Böylece ekran adları için çok fazla güçlük çekmeden çoklu dil desteği alırsınız.

TypeConverter'ın ConvertFrom / ConvertTo yöntemlerine bakın ve enum alanlarınızdaki öznitelikleri okumak için yansımayı kullanın .


Tamam, bir kod yazdım (bu soruya cevabıma bakın) - bunun yeterli olduğunu mu düşünüyorsunuz, bir şey mi kaçırıyorum?
Shalom Craimer

1
Güzel bir. Genel olarak daha iyi, ancak sıradan bir yazılım parçası için asla küreselleşemeyecek olan yazılım parçanız için aşırı olabilir. (Biliyorum, bu varsayım daha sonra yanlış çıkacak. ;-))
peSHIr

85

ComboBoxihtiyacınız olan her şeye sahiptir: FormattingEnabledayarlamanız gereken özellik trueve Formatistediğiniz biçimlendirme mantığını yerleştirmeniz gereken olay. Böylece,

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

Bu yalnızca veri bağlantılı birleşik giriş kutularında mı çalışır? Aksi takdirde Format olayının tetiklenmesini sağlayamıyorum.
SomethingBetter

Buradaki tek sorun, listeyi mantığınıza göre
sıralayamamanızdır

Bu harika bir çözüm. Bir düşünce ile çalışmasına ihtiyacım olacak DataGridComboBoxColumn. Çözme şansı var mı? Ben erişmek için bir yol bulmak mümkün değilim ComboBoxait DataGridComboBoxColumn.
Soko

46

Yapma! Numaralandırmalar temel nesnelerdir ve UI nesneleri değildir - .ToString () 'de UI'ye hizmet etmelerini sağlamak, tasarım açısından oldukça kötü olacaktır. Buradaki yanlış sorunu çözmeye çalışıyorsunuz: asıl sorun, Enum.ToString () 'in açılan kutuda görünmesini istememenizdir!

Şimdi bu gerçekten çözülebilir bir problem! Birleşik giriş kutusu öğelerinizi temsil edecek bir UI nesnesi oluşturursunuz:

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

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

Ve sonra bu sınıfın örneklerini açılan kutunuzun Öğeler koleksiyonuna ekleyin ve şu özellikleri ayarlayın:

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

1
Tüm kalbimle katılıyorum. ToString () sonucunu kullanıcı arayüzüne göstermemelisiniz. Ve yerelleştirme alamazsınız.
Øyvind Skaar

Bunun eski olduğunu biliyorum ama bu nasıl farklı?
nportelli

2
Ben de benzer bir gördüğüm çözümü bunun yerine özel bir sınıf kullanmak yerine, onlar için enum değerleri eşleştirilmiş Dictionaryve kullanılmış Keyve Valuegibi özellikleri DisplayMemberve ValueMember.
Jeff B

42

TypeConverter. Sanırım aradığım şey bu. Selam Simon Svensson !

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

Mevcut numaramda değiştirmem gereken tek şey bu satırı bildirimlerinden önce eklemek.

[TypeConverter(typeof(EnumToStringUsingDescription))]

Bunu yaptığımda, herhangi bir numaralandırma DescriptionAttribute kendi alanlarını .

Oh, ve TypeConverterşu şekilde tanımlanacaktır:

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

Bu, ComboBox durumumda bana yardımcı oluyor, ancak açıkçası aslında ToString(). Sanırım bu arada buna razı olacağım ...


3
Enum -> String işlemlerini yapıyorsunuz, ancak tam bir uygulama istiyorsanız Enum> InstanceDescriptor ve String -> Enum'a da ihtiyacınız olacak. Ama sanırım şu anda bunu sergilemek sizin ihtiyaçlarınız için yeterli. ;)
sisve

1
Bu çözüm, maalesef açıklamalarınız statik olduğunda işe yarar.
Llyle 01

Bu arada, TypeConverter kullanımı statik tanımlamalara bağlı değildir, iş ortağı, özniteliklerden başka kaynaklardan değerleri doldurabilir.
Dmitry Tashkinov

3
Birkaç saattir saçımı çekiyorum, ancak basit konsol uygulamalarında bile işe yaramıyor gibi görünüyor. Numarayı süsledim [TypeConverter(typeof(EnumToStringUsingDescription))] public enum MyEnum {[Description("Blah")] One}, yapmayı denedim Console.WriteLine(MyEnum.One)ve hala "Bir" olarak çıkıyor. TypeDescriptor.GetConverter(typeof(MyEnum)).ConvertToString(MyEnum.One)(Hangisi iyi çalışıyor) gibi özel bir sihire ihtiyacınız var mı?
Dav

1
@scraimer Kod destekleyen bayraklarınızın bir sürümünü yayınladım. tüm hakları size aittir ...
Avi Turner

32

Numaralandırma örneğinizi kullanarak:

using System.ComponentModel;

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

Bir Uzantı Oluşturun:

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

O zaman aşağıdaki gibi bir şey kullanabilirsiniz:

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

Görmek: fazla bilgi için http://www.blackwasp.co.uk/EnumDescription.aspx . Çözüm için kredi Richrd Carr'a gidiyor


Yönlendirilen sitedeki ayrıntıları takip ettim ve aşağıdaki gibi kullandım, benim için basit görünüyor 'string myDesc = HowNice.ReallyNice.Description ();' myDesc, Really Nice
Ananda

8

Açıklamaları olan tüm numaralandırmalarınız için kullanabileceğiniz genel bir yapı oluşturabilirsiniz. Sınıfa ve sınıfa örtük dönüştürmelerle, değişkenleriniz ToString yöntemi dışında hala enum gibi çalışır:

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

}

Kullanım örneği:

Described<HowNice> nice = HowNice.ReallyNice;

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

5

Bunu farklı bir türe bağlamadan yapabileceğinizi sanmıyorum - en azından uygun şekilde değil. Normalde, kontrol edemezseniz bile, özel biçimlendirme yapmak ToString()için a kullanabilirsiniz TypeConverter- ancak IIRCSystem.ComponentModel numaralandırmalar için buna saygı göstermez.

string[]Açıklamalardan birine mi yoksa temelde anahtar / değer çifti gibi bir şeye mi bağlanabilirsiniz ? (açıklama / değer) - şöyle bir şey:

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

Ve sonra bağlan EnumWrapper<HowNice>.GetValues()


1
'GetDescription' adı mevcut bağlamda mevcut değil. .NET 4.0 kullanıyorum
Muhammad Adeel Zahid

@MuhammadAdeelZahid, sorunun başlangıcına yakından bakıyor - bağlantılı gönderiden geliyor: stackoverflow.com/questions/479410/enum-tostring
Marc Gravell

üzgünüm ama sorudan hiçbir ipucu alamıyorum. yönteminiz derlenmiyor ve hatayı gösteriyor.
Muhammad Adeel Zahid

Merhaba Marc, fikrini denedim. Çalışıyor, ama kendisi yerine theComboBox.SelectItemis türü . Sanırım dolandırıcı istiyor . EnumWrapper<T>TReading (HowNice)myComboBox.selectedItem will return the selected value as the enum value.
Peter Lee

5

Bunu yapmanın en iyi yolu bir sınıf oluşturmaktır.

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

Bunun en iyi yol olduğuna inanıyorum.

Combobox'lara doldurulduğunda güzel ToString gösterilecek ve hiç kimsenin sınıfınızın daha fazla örneğini yapamayacağı gerçeği onu bir numaralandırma yapar.

ps orada bazı küçük sözdizimi düzeltmeleri gerekebilir, C # konusunda çok iyi değilim. (Java adamı)


1
Bu, birleşik giriş kutusu sorununa nasıl yardımcı olur?
peSHIr

Pekala, şimdi, yeni nesne birleşik giriş kutusuna yerleştirildiğinde, ToString'i düzgün bir şekilde görüntülenecek ve sınıf yine de bir enum gibi davranacaktır.
jjnguy

1
Benim cevabım da olurdu.
Mikko Rantanen

3
Ve orijinal posterin açıkça bir ders istemediğini görmek. Bir dersin o kadar fazla iş olduğunu sanmıyorum. Tüm numaralandırmalar için açıklamayı ve ToString geçersiz kılmayı bir üst sınıfa soyutlayabilirsiniz. Bundan sonra ihtiyacınız olan tek şey bir kurucu private HowNice(String desc) : base(desc) { }ve statik alanlar.
Mikko Rantanen

Bundan kaçınmayı umuyordum, çünkü yaptığım her sayımın kendi sınıfını gerektireceği anlamına geliyor. Ugh.
Shalom Craimer

3

Her bir enum için bir sınıf oluşturmayı tercih etmediğiniz göz önüne alındığında, enum değeri / ekran metninden oluşan bir sözlük oluşturmanızı ve bunun yerine bunu bağlamanızı öneririm.

Bunun, orijinal gönderideki GetDescription yöntem yöntemlerine bir bağımlılığı olduğunu unutmayın.

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

İyi fikir. Ama bunu bir birleşik giriş kutusuyla nasıl kullanırım? Kullanıcı birleşik giriş kutusundan bir öğe seçtikten sonra, hangi öğeleri seçtiğini nasıl bilebilirim? Açıklama dizesine göre aransın mı? Bu da
cildimi

Seçilen öğenin Anahtarı, gerçek enum değeri olacaktır. Ayrıca, açıklama dizelerini çarpışmayın - kullanıcı farkı nasıl anlayacak?
Richard Szalay

<cont> çarpışan açıklama dizeleriniz varsa, numaralandırmanın değerlerini yine de doğrudan birleşik giriş kutusuna bağlamamalısınız.
Richard Szalay

hmmm ... Pekala, birleşik giriş kutusuna nasıl öğe ekleyeceğinize dair örnek bir kod verebilir misiniz? Tek düşünebildiğim "foreach (descripationsDict.Values ​​içindeki string s) {this.combobox.Items.Add (s);}"
Shalom Craimer

1
ComboBox.DataSource = sözlük;
Richard Szalay

3

C # 'da numaralandırmaların ToString () değerini geçersiz kılmak mümkün değildir. Ancak, uzatma yöntemlerini kullanabilirsiniz;

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

Elbette yönteme açık bir çağrı yapmanız gerekecek, yani;

HowNice.ReallyNice.ToString(0)

Bu, switch deyimiyle falan güzel bir çözüm değil - ama çalışmalı ve umarım birçok yeniden yazıma izin vermeli ...


Numaranıza bağlanan denetimin bu uzantı yöntemini çağırmayacağını, varsayılan uygulamayı çağıracağını unutmayın.
Richard Szalay

Sağ. Dolayısıyla, bir yerde bir tanıma ihtiyacınız varsa, bu geçerli bir seçenektir, ortaya çıkan birleşik giriş kutusu sorununa yardımcı olmaz.
peSHIr

Daha büyük bir sorun, bunun asla çağrılmamasıdır (bir uzantı yöntemi olarak) - zaten var olan örnek yöntemleri her zaman önceliklidir.
Marc Gravell

Tabii ki Marc haklı (her zamanki gibi?). .NET deneyimim minimum düzeydedir, ancak yönteme sahte bir parametre sağlamak işe yaramalıdır. Cevap düzenlendi.
Björn

2

@Scraimer yanıtını takiben, burada bayrakları da destekleyen enum-string tip dönüştürücünün bir sürümü:

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

}

Ve kullanmak için bir uzatma yöntemi:

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

Herhangi bir türle kullanmak için genel bir sınıf yazardım. Geçmişte buna benzer bir şey kullandım:

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
}

Buna ek olarak, bir enum türü verilen birleşik giriş kutusu öğelerinin bir listesini oluşturmak için statik bir "fabrika yöntemi" ekleyebilirsiniz (orada sahip olduğunuz GetDescriptions yöntemi ile hemen hemen aynı). Bu, sizi her bir numaralandırma türü için bir varlık uygulama zorunluluğundan kurtarır ve ayrıca "GetDescription" yardımcı yöntemi için güzel / mantıksal bir yer sağlar (şahsen ben buna FromEnum (T obj) derim ...


1

İhtiyacınız olanı içeren bir koleksiyon oluşturun ( enum değerini içeren bir Valueözelliği içeren basit nesneler ve birleşimi içeren ve bu koleksiyona veri tabanı oluşturma özelliği gibi.HowNiceDescriptionGetDescription<HowNice>(Value)

Bunun gibi biraz:

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

böyle bir koleksiyon sınıfınız olduğunda:

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

}

Gördüğünüz gibi, bu koleksiyon, bahsettiğiniz işlevi stringkullanmak yerine numaralandırıcınızın bir alt kümesini seçmek ve / veya özel bir biçimlendirme uygulamak için lambda'lar ile kolayca özelleştirilebilir GetDescription<T>(x).


Mükemmel, ancak kodda daha az çalışma gerektiren bir şey arıyorum.
Shalom Craimer

Tüm numaralandırıcılarınız için bu tür şeyler için aynı genel koleksiyonu kullanabilseniz bile? Elbette, her bir enum için özel bir koleksiyon yazmayı önermiyordum.
peSHIr

1

Enum.ToString'i hedeflemek ve istediğiniz tüm kodu eklemek için PostSharp'ı kullanabilirsiniz. Bu, herhangi bir kod değişikliği gerektirmez.


1

İhtiyacınız olan şey, bir numaralandırmayı ReadonlyCollection'a dönüştürmek ve koleksiyonu birleşik giriş kutusuna (veya bu konuda Anahtar-Değer Çifti etkinleştirilmiş herhangi bir denetime) bağlamaktır.

Öncelikle, listenin öğelerini içeren bir sınıfa ihtiyacınız var. İhtiyacınız olan tek şey int / string çifti olduğundan, işlevselliği istediğiniz herhangi bir nesnede uygulayabilmeniz için bir arabirim ve temel sınıf birleşik kullanılmasını öneririm:

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
}

İşte arabirim ve onu uygulayan örnek bir sınıf. 'Anahtar' sınıfının Enum'a güçlü bir şekilde yazıldığına ve IValueDescritionItem profillerinin açık bir şekilde uygulandığına dikkat edin (böylece sınıf her türlü özelliğe sahip olabilir ve bunu uygulayanları SEÇEBİLİRSİNİZ. Anahtar / Değer çifti.

Şimdi EnumToReadOnlyCollection sınıfı:

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

}

Yani kodunuzda ihtiyacınız olan tek şey:

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

Koleksiyonunuzun MyItem ile yazıldığını unutmayın; bu nedenle, uygun proprtie'ye bağlanırsanız, combobox değeri bir enum değeri döndürmelidir.

Koleksiyonu basit bir birleşik sarf malzemesinden daha kullanışlı hale getirmek için T this [Enum t] özelliğini ekledim, örneğin textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;

Elbette, MyItem'i yalnızca bu puprose için kullanılan bir Anahtar / Değer sınıfına dönüştürmeyi seçebilir ve EnumToReadnlyCollection öğesinin tür bağımsız değişkenlerindeki Öğem'i etkin bir şekilde atlayabilirsiniz, ancak daha sonra anahtar için int ile gitmek zorunda kalırsınız (yani combobox1.SelectedValue enum türü değil int döndürür). MyItem'i değiştirmek için bir KeyValueItem sınıfı oluşturursanız, bunun etrafında çalışırsınız vb.


1

Bu eski ileti dizisini açtığım için üzgünüm.

Bu örnekteki bir açılır liste metin alanı aracılığıyla yalnızca açıklama değil, kullanıcıya anlamlı ve yerelleştirilmiş değerler görüntüleyebildiğinden, numaralandırmayı yerelleştirmek için aşağıdaki yolu izlerim.

İlk olarak, global bir kaynaklar dosyasından yerelleştirilmiş dizeler almak için OwToStringByCulture adlı basit bir yöntem oluşturdum, bu örnekte App_GlobalResources klasöründeki BiBongNet.resx. Bu kaynak dosyasının içinde, tüm dizelerin enum değerleriyle aynı olduğundan emin olun (ReallyNice, SortOfNice, NotNice). Bu yöntemde, genellikle kaynak dosyasının adı olan: resourceClassName parametresini geçiyorum.

Ardından, OwFillDataWithEnum adlı bir açılır listeyi veri kaynağı olarak enum ile doldurmak için statik bir yöntem oluşturuyorum. Bu yöntem daha sonra herhangi bir numaralandırma ile kullanılabilir.

Daha sonra DropDownList1 adlı bir açılır listeye sahip sayfada, aşağı açılır listeye enumu doldurmak için Page_Load'da aşağıdaki basit bir kod satırını ayarladım.

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

Bu kadar. Sanırım bunun gibi bazı basit yöntemlerle, herhangi bir liste denetimini herhangi bir enum ile, sadece açıklayıcı değerler olarak değil, görüntülenecek yerelleştirilmiş metinle doldurabilirsiniz. Tüm bu yöntemleri daha iyi kullanım için uzatma yöntemleri olarak yapabilirsiniz.

Umarım bu yardımcı olur. Paylaşmak için paylaşın!

Yöntemler şunlardır:

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
}

Bunu çözmek için, bir uzantı Yöntemi ve aşağıdaki gibi bir Dizeler Dizisi kullanmalısınız:

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

Basit kod ve hızlı kod çözme.


dizeler değişkeni Statik olmalı ve şu şekilde bildirilmelidir: Statik Dize [] dizeler = yeni [] {...}
Sérgio

Bununla ilgili tek sorun, her numaralandırma için bir işleve sahip olmanızın gerekmesi ve açıklamanın numaralandırmanın kendisinden ayrı olması ...
Avi Turner

1

Bu yaklaşımı denedim ve benim için çalıştı.

Numaralandırmalar için bir sarmalayıcı sınıfı yarattım ve örtük operatörü enum değişkenlerine atayabilmek için aşırı yükledim (benim durumumda bir nesneyi bir ComboBoxdeğere bağlamam gerekiyordu ).

Enum değerlerini istediğiniz şekilde biçimlendirmek için yansımayı kullanabilirsiniz, benim durumumda DisplayAttributeenum değerlerinden (varsa) dışarı çıkıyorum.

Bu yardımcı olur umarım.

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

DÜZENLE:

Her ihtimale karşı, benim hemen aşağıdaki işlevi kullanın enumben için kullandığınız değerleri DataSourcearasındaComboBox

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

Bir kez GetDescriptionyöntemini, bir uzantısı yöntemiyle kullanabiliriz (küresel statik olması gerekiyor):

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
Stackoverflow'a hoş geldiniz!
Gönderi

-1

Enum'u şu şekilde tanımlayabilirsiniz:

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

ve sonra kullanın HowNice.GetStringValue().


2
Bu derlenmiyor (bende .NET 3.5 var). "StringValue" nerede beyan edilir?
korku

1
@ Scraimer'den gelen cevap aynıdır, tek fark onun çerçevenin dışında bir niteliği kullanması, oysa siz bir çeşit kendi kendini tanımlayan özniteliği kullanıyorsunuz.
Oliver
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.