열거 형 값에 대한 사용자 지정 문자열 형식을 가진 열거 형 콤보 상자를 사용하려면 어떻게합니까?


135

Post Enum ToString 에서 메소드는 다음 DescriptionAttribute과 같이 사용자 정의 속성을 사용하도록 설명됩니다 .

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

그런 다음 다음 GetDescription과 같은 구문을 사용하여 function을 호출합니다 .

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

그러나 ComboBox를 강제로 호출 할 수 없기 때문에 단순히 열거 형 값으로 ComboBox를 채우려는 경우GetDescription 실제로 도움이되지 않습니다 .

내가 원하는 것은 다음 요구 사항이 있습니다.

  • 판독 (HowNice)myComboBox.selectedItem하면 선택한 값이 열거 형 값으로 반환됩니다.
  • 열거 값의 이름뿐만 아니라 사용자에게 친숙한 표시 문자열이 표시되어야합니다. 따라서 " NotNice" 를 보는 대신 " "가 표시 Not Nice At All됩니다.
  • 다행히 솔루션이 기존 열거에 대한 코드 변경을 최소화해야합니다.

분명히, 내가 만든 각 열거 형에 대해 새로운 클래스를 구현하고 재정의 할 수 ToString()는 있지만 각 열거 형에 많은 작업이 필요하므로 오히려 피해야합니다.

어떤 아이디어?

도대체, 나는 현상금 으로 포옹 을 던질 것입니다 :-)


1
jjnguy는 Java 열거 형 이이 문제를 훌륭하게 해결한다는 것이 정확 하지만 ( javahowto.blogspot.com/2006/10/… ) 의심스러운 관련성이 있습니다.
Matthew Flaschen

8
자바 열거 형은 농담입니다. 아마도 그들은 2020 년에 속성을 추가 할 것입니다 : /
Chad Grant

더 가벼운 (그러나 논란의 여지가 적은) 솔루션은 thread를 참조하십시오 .
Gutblender

답변:


42

지정된 속성을 읽어 리소스에서 찾아 보는 TypeConverter를 작성할 수 있습니다. 따라서 많은 번거 로움없이 표시 이름에 대한 다국어 지원을받을 수 있습니다.

TypeConverter의 ConvertFrom / ConvertTo 메서드를 살펴보고 리플렉션을 사용하여 열거 형 필드의 특성을 읽습니다 .


좋아, 나는 약간의 코드를 썼다 (이 질문에 대한 나의 대답을보십시오)-충분하다고 생각합니까, 뭔가 빠졌습니까?
Shalom Craimer

1
좋은데 더 나은 오버올 (overal)은 좋지만 세계 어디에서나 세계화 될 소프트웨어의 경우에는 과도 할 수 있습니다. (나는 나중에 가정이 잘못된 것으로 판명된다는 것을 안다. ;-))
peSHIr

85

ComboBoxFormattingEnabled설정해야하는 속성 trueFormat원하는 형식 지정 로직을 배치해야하는 이벤트 등 필요한 모든 것이 있습니다 . 그러므로,

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

이것은 데이터 바운드 콤보 상자에서만 작동합니까? 그렇지 않으면 Format 이벤트를 발생시킬 수 없습니다.
무언가 더

여기서 유일한 문제는 논리에 따라 목록을 정렬 할 수
없다는 것입니다.

이것은 훌륭한 솔루션입니다. DataGridComboBoxColumn그래도 작동하려면 필요합니다 . 그것을 해결할 기회가 있습니까? 나는에 대한 액세스를 얻을 수있는 방법 찾을 수 아니에요 ComboBox의를 DataGridComboBoxColumn.
Soko

46

하지마! 열거 형은 UI 객체가 아닌 프리미티브이며, .ToString ()에서 UI를 제공하도록 만드는 것은 디자인 관점에서 상당히 나쁩니다. 여기에서 잘못된 문제를 해결하려고합니다. 실제 문제는 Enum.ToString ()이 콤보 상자에 나타나지 않게하는 것입니다!

이제 이것은 실제로 매우 해결 가능한 문제입니다! 콤보 상자 항목을 나타내는 UI 객체를 만듭니다.

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

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

그런 다음이 클래스의 인스턴스를 콤보 상자의 Items 컬렉션에 추가하고 다음 속성을 설정하십시오.

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

1
나는 전적으로 동의합니다. ToString ()의 결과를 UI에 노출해서는 안됩니다. 그리고 당신은 현지화를 얻지 못합니다.
Øyvind Skaar

나는 이것이 오래되었다는 것을 알고 있지만 어떻게 다른가?
nportelli

2
나는 비슷한 본 적이 솔루션 대신 사용자 정의 클래스를 사용하는, 그들이에 열거 값을 매핑 Dictionary하고, 사용 Key하고 Value는 AS 속성을 DisplayMember하고 ValueMember.
Jeff B

42

TypeConverter. 나는 이것이 내가 찾고있는 것이라고 생각합니다. 모든 우박 사이먼 스벤손 !

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

현재 열거 형에서 변경해야 할 것은 선언 전에이 줄을 추가하는 것입니다.

[TypeConverter(typeof(EnumToStringUsingDescription))]

나는 그렇게되면, 어떤 열거는 사용하여 표시 얻을 것이다 DescriptionAttribute그 필드를.

아, 그리고 TypeConverter다음과 같이 정의됩니다 :

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

이것은 ComboBox 사례에 도움이되지만 실제로 실제로를 재정의하지는 않습니다 ToString(). 그동안이 문제를 해결하겠다고 생각합니다.


3
Enum-> String을 처리하고 있지만 완전한 구현을 원하면 Enum> InstanceDescriptor 및 String-> Enum도 필요합니다. 그러나 현재 귀하의 요구에 맞게 표시하는 것으로 충분합니다. ;)
sisve

1
이 솔루션은 설명이 정적 일 때만 작동합니다.
Llyle

그건 그렇고, TypeConverter의 사용은 정적 설명에 구속되지 않으며, Coverter는 속성 이외의 다른 소스에서 값을 채울 수 있습니다.
Dmitry Tashkinov

3
지금 몇 시간 동안 머리를 잡아 당겼지만 간단한 콘솔 앱에서도 여전히 작동하지 않는 것 같습니다. 나는 열거 형을 장식하고 [TypeConverter(typeof(EnumToStringUsingDescription))] public enum MyEnum {[Description("Blah")] One}, 시도했지만 Console.WriteLine(MyEnum.One)여전히 "하나"로 나옵니다. TypeDescriptor.GetConverter(typeof(MyEnum)).ConvertToString(MyEnum.One)(정상적으로 작동하는) 과 같은 특별한 마술이 필요 합니까?
Dav

1
@ scraimer 플래그를 지원하는 코드 버전을 게시했습니다. 판권 소유 ...
Avi Turner

32

열거 형 예제 사용 :

using System.ComponentModel;

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

확장을 만듭니다 :

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

그런 다음 다음과 같은 것을 사용할 수 있습니다.

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

자세한 내용은 http://www.blackwasp.co.uk/EnumDescription.aspx 를 참조하십시오. 신용은 솔루션을 위해 Richrd Carr에게갑니다


참조 된 사이트의 세부 정보를 따라 다음과 같이 사용했습니다 .'string myDesc = HowNice.ReallyNice.Description (); ' myDesc는 Really Nice
Ananda를

8

설명이있는 모든 열거 형에 사용할 수있는 일반적인 구조체를 만들 수 있습니다. 클래스와의 암시 적 변환을 통해 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;
    }

}

사용 예 :

Described<HowNice> nice = HowNice.ReallyNice;

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

5

최소한 다른 유형에 바인딩하지 않고는 할 수 있다고 생각하지 않습니다. 적어도 편리하지는 않습니다. 일반적으로 제어 할 수없는 경우에도 ToString()a TypeConverter를 사용하여 사용자 정의 형식을 지정할 수 있지만 IIRC는 System.ComponentModel열거 형에 대해 이것을 존중하지 않습니다.

string[]설명 중 하나 또는 본질적으로 키 / 값 쌍과 같은 것에 바인딩 할 수 있습니까? (설명 / 값)-다음과 같은 것 :

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

그리고 바인딩 EnumWrapper<HowNice>.GetValues()


1
현재 컨텍스트에 'GetDescription'이라는 이름이 없습니다. .NET 4.0을 사용하는 메신저
Muhammad Adeel Zahid

@MuhammadAdeelZahid 질문의 시작 부분을 자세히 살펴보십시오-링크 된 게시물에서 가져옵니다 : stackoverflow.com/questions/479410/enum-tostring
Marc Gravell

미안하지만 질문에서 단서를 얻을 수 없습니다. 메소드가 컴파일되지 않고 오류가 표시됩니다.
Muhammad Adeel Zahid

안녕 마크, 나는 당신의 아이디어를 시도했다. 작동하지만 자체 대신 theComboBox.SelectItem유형입니다 . 나는 scraimer가 원한다고 생각한다 . EnumWrapper<T>TReading (HowNice)myComboBox.selectedItem will return the selected value as the enum value.
피터 리

5

가장 좋은 방법은 수업을하는 것입니다.

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

나는 그것이 최선의 방법이라고 믿습니다.

콤보 상자에 채워지면 예쁜 ToString이 표시되며 아무도 클래스의 인스턴스를 더 이상 만들 수 없다는 사실이 본질적으로 열거 형이됩니다.

추신 : 약간의 구문 수정이 필요할 수 있습니다 .C #에는 좋지 않습니다. (자바 남자)


1
이것이 콤보 박스 문제에 어떻게 도움이됩니까?
peSHIr

이제 새 객체를 콤보 상자에 넣으면 ToString이 올바르게 표시되고 클래스는 여전히 열거 형처럼 작동합니다.
jjnguy

1
내 대답이었을 것입니다.
Mikko Rantanen

3
그리고 원래 포스터가 명시 적으로 수업을 원하지 않는 방법을 봅니다. 나는 수업이 훨씬 더 많은 일이라고 생각하지 않습니다. 설명과 ToString을 모든 열거 형의 상위 클래스로 재정의 할 수 있습니다. 이 후에는 생성자 private HowNice(String desc) : base(desc) { }와 정적 필드 만 있으면 됩니다.
Mikko Rantanen

내가 만드는 모든 열거에는 자체 클래스가 필요하기 때문에 이것을 피하기를 바랐습니다. 어.
Shalom Craimer

3

각 열거 형에 대해 클래스를 만들지 않기 때문에 열거 형 값 / 표시 텍스트의 사전을 만들고 대신 바인딩하는 것이 좋습니다.

이것은 원래 게시물의 GetDescription 메서드 메서드에 종속됩니다.

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

좋은 생각. 그러나 이것을 콤보 박스와 함께 어떻게 사용합니까? 사용자가 콤보 상자에서 항목을 선택하면 그가 선택한 항목을 어떻게 알 수 있습니까? 설명 문자열로 검색 하시겠습니까? 즉 (설명 문자열 사이에 문자열 "충돌"가있을 수 있습니다) 내 피부 가려움증을 만든다
샬롬 Craimer

선택한 항목의 키는 실제 열거 형 값입니다. 또한 설명 문자열을 충돌시키지 마십시오. 사용자는 차이점을 어떻게 알 수 있습니까?
Richard Szalay

<cont> 충돌하는 설명 문자열이 있으면 어쨌든 열거 형 값을 콤보 상자에 직접 바인딩해서는 안됩니다.
Richard Szalay

흠 ... 글쎄, 콤보 박스에 아이템을 추가하는 방법에 대한 예제 코드를 제공해 주시겠습니까? 내가 생각할 수있는 것은 "foreach (descriptionsDict.Values의 문자열 s) {this.combobox.Items.Add (s);}"
Shalom Craimer

1
ComboBox.DataSource = 사전;
Richard Szalay

3

C #에서 열거 형의 ToString ()을 재정의 할 수 없습니다. 그러나 확장 방법을 사용할 수 있습니다.

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

물론 메소드를 명시 적으로 호출해야합니다.

HowNice.ReallyNice.ToString(0)

이것은 switch 문과 모든 것을 갖춘 훌륭한 솔루션은 아니지만 많은 재 작성을 위해 잘 작동해야합니다 ...


열거 형에 바인딩하는 컨트롤은이 확장 메서드를 호출하지 않고 기본 구현을 호출합니다.
Richard Szalay

권리. 따라서 어딘가에 설명이 필요한 경우 실행 가능한 옵션이며, 제기 된 콤보 박스 문제에는 도움이되지 않습니다.
peSHIr

더 큰 문제는 이것이 확장 메서드로 호출 되지 않는다는 것 입니다. 이미 존재하는 인스턴스 메서드는 항상 우선합니다.
Marc Gravell

물론 Marc가 옳습니다 (항상 그렇습니까?). 내 .NET 경험은 최소한이지만 메서드에 더미 매개 변수를 제공하면 트릭을 수행해야합니다. 수정 된 답변.
Björn

2

@ scraimer 답변에 이어 플래그를 지원하는 enum-to-string 유형 변환기 버전이 있습니다.

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

}

그리고 그것을 사용하기위한 확장 방법 :

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

모든 유형에 사용할 일반 클래스를 작성합니다. 나는 과거에 이와 같은 것을 사용했다 :

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
}

이 외에도 정적 "공장 메소드"를 추가하여 열거 형 유형이 지정된 콤보 박스 항목 목록을 작성할 수 있습니다 (여기서는 GetDescriptions 메소드와 거의 동일 함). 이렇게하면 각 열거 형 유형마다 하나의 엔터티를 구현하지 않아도되고 "GetDescriptions"헬퍼 메서드에 대한 좋은 / 논리적 장소를 제공합니다 (개인적으로 FromEnum (T obj)이라고합니다 ...


1

열거 형 값을 포함하는 Value속성을 포함하는 간단한 개체 와 해당 컬렉션에 콤보를 포함 하고 데이터 바인딩 HowNice하는 Description속성 과 같이 필요한 것을 포함하는 컬렉션을 만듭니다 GetDescription<HowNice>(Value).

이런 식으로 :

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

다음과 같은 컬렉션 클래스가있을 때 :

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

}

보다시피,이 컬렉션은 람다로 쉽게 사용자 정의하여 열거 자의 하위 집합을 선택하거나 언급 stringGetDescription<T>(x)함수를 사용하는 대신 사용자 정의 형식을 구현할 수 있습니다.


훌륭하지만 코드에서 더 적은 작업이 필요한 것을 찾고 있습니다.
Shalom Craimer

모든 열거 자에 대해 이런 종류의 일에 동일한 일반 컬렉션을 사용할 수 있습니까? 물론 각 열거 형에 대해 이러한 컬렉션을 사용자 지정 작성하는 것은 제안하지 않았습니다.
peSHIr

1

PostSharp를 사용하여 Enum.ToString을 대상으로하고 원하는 aditionall 코드를 추가 할 수 있습니다. 코드를 변경할 필요가 없습니다.


1

열거 형을 ReadonlyCollection으로 바꾸고 컬렉션을 콤보 상자 (또는 그 문제에 대한 키-값 쌍 사용 컨트롤)에 바인딩하는 것이 필요합니다.

먼저 목록의 항목을 포함하는 클래스가 필요합니다. 필요한 것은 int / string 쌍이므로 인터페이스와 기본 클래스 콤보를 사용하여 원하는 객체에서 기능을 구현할 수 있습니다.

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
}

클래스의 Key는 Enum에 강력하게 입력되고 IValueDescritionItem 속성은 명시 적으로 구현됩니다 (클래스에 모든 속성이있을 수 있으며 클래스를 구현하는 속성을 선택할 수 있음). 키 / 값 쌍.

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

}

따라서 코드에서 필요한 것은 다음과 같습니다.

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

컬렉션은 MyItem으로 입력되므로 적절한 속성에 바인딩하면 콤보 상자 값이 열거 형 값을 반환해야합니다.

T this [Enum t] 속성을 추가하여 간단한 콤보 소모품보다 컬렉션을 더욱 유용하게 만듭니다 (예 : textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;

물론 EnumToReadnlyCollection의 유형 인수에서 MyItem을 효과적으로 건너 뛰는이 puprose에만 사용되는 Key / Value 클래스로 MyItem을 전환하도록 선택할 수 있지만 키에 대해 int로 이동해야합니다 (combobox1.SelectedValue를 얻는 것을 의미 함) 열거 형이 아닌 int를 반환합니다). MyItem 등을 대체하기 위해 KeyValueItem 클래스를 작성하는 경우이 문제를 해결하십시오.


1

이 오래된 실을 꺼내서 죄송합니다.

열거 형을 현지화하는 다음 방법 으로이 예제의 드롭 다운 목록 텍스트 필드를 통해 설명뿐만 아니라 사용자에게 의미 있고 현지화 된 값을 표시 할 수 있습니다.

먼저 전역 리소스 파일에서 지역화 된 문자열을 가져 오기 위해 OwToStringByCulture라는 간단한 메서드를 만듭니다.이 예제에서는 App_GlobalResources 폴더의 BiBongNet.resx입니다. 이 리소스 파일 내에서 모든 문자열이 열거 형 값 (ReallyNice, SortOfNice, NotNice)과 동일한 지 확인하십시오. 이 방법에서는 일반적으로 리소스 파일의 이름 인 resourceClassName 매개 변수를 전달합니다.

다음으로 드롭 다운 목록을 데이터 소스로 OwFillDataWithEnum이라고하는 드롭 다운 목록을 채우는 정적 메소드를 작성합니다. 이 방법은 나중에 열거 형과 함께 사용할 수 있습니다.

그런 다음 DropDownList1이라는 드롭 다운 목록이있는 페이지에서 Page_Load에서 드롭 다운 목록에 열거 형을 채우는 간단한 코드 한 줄을 다음과 같이 설정했습니다.

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

그게 다야. 이와 같은 간단한 방법을 사용하면 설명 값뿐만 아니라 현지화 된 텍스트로 목록 컨트롤을 열거 형으로 채울 수 있습니다. 이러한 방법을 모두 확장 방법으로 사용하여 더 잘 사용할 수 있습니다.

이 도움을 바랍니다. 공유하려면 공유하십시오!

방법은 다음과 같습니다.

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
}

이 문제를 해결하려면 확장 메서드와 문자열 배열을 사용해야합니다.

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

간단한 코드와 빠른 디코딩.


문자열 변수는 정적이어야하며 다음과 같이 선언되어야합니다. 정적 문자열 [] strings = new [] {...}
Sérgio

이 유일한 문제는, 당신은 모든 열거하는 기능이 필요하며, 설명은 ... 열거 자체의 간격이 될 것입니다
아비 터너

1

나는이 접근법을 시도했고 그것은 나를 위해 일했다.

열거 형에 대한 래퍼 클래스를 만들고 암시 적 연산자를 오버로드하여 열거 형 변수에 할당 할 수 있도록했습니다 (제 경우에는 객체를 ComboBox값 에 바인딩해야했습니다 ).

리플렉션을 사용하여 원하는 방식으로 열거 형 값의 형식을 지정할 수 있습니다. 제 경우 DisplayAttribute에는 열거 형 값에서 (존재하는 경우)를 검색합니다 .

도움이 되었기를 바랍니다.

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

편집하다:

경우에 따라 다음 함수를 사용하여에 대한 enum값 을 가져옵니다 DataSource.ComboBox

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

당신은 일단 GetDescription방법을, 당신은 확장 메서드를 통해이를 사용할 수 있습니다 (이것은 글로벌 정적 필요) :

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에 오신 것을 환영합니다! 샘플 코드는 포스트 정확도 : 개선하는 것은 간단한 설명을 제공하기 위해 항상 더 나은
Picrofo 소프트웨어

-1

Enum을 다음과 같이 정의 할 수 있습니다.

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

을 사용하십시오 HowNice.GetStringValue().


2
이것은 컴파일되지 않습니다 (.NET 3.5가 있습니다). 'StringValue'는 어디에 선언되어 있습니까?
awe

1
@scraimer의 대답은 프레임 워크 외부에서 속성을 사용하고 있지만 자체 정의 된 속성을 사용한다는 점을 제외하면 동일합니다.
Oliver
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.