ฉันจะมีคอมโบบ็อกซ์ที่มีขอบเขต enum พร้อมการจัดรูปแบบสตริงที่กำหนดเองสำหรับค่า enum ได้อย่างไร


136

ในโพสต์Enum ToStringมีการอธิบายวิธีการใช้แอตทริบิวต์ที่กำหนดเองDescriptionAttributeดังนี้:

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

จากนั้นคุณเรียกใช้ฟังก์ชันGetDescriptionโดยใช้ไวยากรณ์เช่น:

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

แต่นั่นไม่ได้ช่วยฉันจริงๆเมื่อฉันต้องการเติม ComboBox ด้วยค่าของ enum เนื่องจากฉันไม่สามารถบังคับให้ ComboBox โทรGetDescriptionได้

สิ่งที่ฉันต้องการมีข้อกำหนดดังต่อไปนี้:

  • การอ่าน(HowNice)myComboBox.selectedItemจะส่งคืนค่าที่เลือกเป็นค่า enum
  • ผู้ใช้ควรเห็นสตริงการแสดงที่ใช้งานง่ายไม่ใช่แค่ชื่อของค่าการแจงนับ ดังนั้นแทนที่จะเห็น " NotNice" ผู้ใช้จะเห็น " Not Nice At All"
  • หวังว่าการแก้ปัญหาจะต้องมีการเปลี่ยนแปลงโค้ดเล็กน้อยในการแจงนับที่มีอยู่

เห็นได้ชัดว่าฉันสามารถใช้คลาสใหม่สำหรับแต่ละ enum ที่ฉันสร้างขึ้นและลบล้างมันToString()ได้ แต่นั่นเป็นงานที่ต้องทำมากสำหรับแต่ละ enum และฉันควรหลีกเลี่ยง

ความคิดใด ๆ ?

ห่าฉันจะกอดด้วยซ้ำ:-)


1
jjnguy ถูกต้องที่ Java enums แก้ปัญหานี้ได้ดี ( javahowto.blogspot.com/2006/10/… ) แต่นั่นเป็นความเกี่ยวข้องที่น่าสงสัย
Matthew Flaschen

8
Java Enums เป็นเรื่องตลก บางทีพวกเขาอาจจะเพิ่มคุณสมบัติในปี 2020: /
Chad Grant

สำหรับน้ำหนักเบา ( แต่เนื้อหาน้อยที่แข็งแกร่ง) วิธีการแก้ปัญหาดูกระทู้ของฉัน
Gutblender

คำตอบ:


42

คุณสามารถเขียน TypeConverter ที่อ่านแอตทริบิวต์ที่ระบุเพื่อค้นหาในทรัพยากรของคุณ ดังนั้นคุณจะได้รับการสนับสนุนหลายภาษาสำหรับชื่อที่แสดงโดยไม่ต้องยุ่งยากมากนัก

ดูเป็นวิธีการ ConvertFrom / ConvertTo TypeConverter ของและสะท้อนการใช้ในการอ่านแอตทริบิวต์ใน enum ของคุณเขต


ตกลงฉันเขียนโค้ด (ดูคำตอบสำหรับคำถามนี้) - คุณคิดว่าเพียงพอหรือไม่ฉันขาดอะไรไปหรือเปล่า?
Shalom Craimer

1
ทำได้ดีนี่. ดีกว่ามาก แต่อาจจะมากเกินไปสำหรับซอฟต์แวร์ที่คุณไม่เคยใช้งานในระดับสากลในทุก ๆ ทาง (ฉันรู้ว่าสมมติฐานนั้นจะกลายเป็นเท็จในภายหลัง ;-))
peSHIr

85

ComboBoxมีทุกสิ่งที่คุณต้องการ: FormattingEnabledคุณสมบัติที่คุณควรตั้งค่าtrueและFormatเหตุการณ์ที่คุณจะต้องวางตรรกะการจัดรูปแบบที่ต้องการ ด้วยประการฉะนี้

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

สิ่งนี้ใช้ได้กับ databound comboboxes เท่านั้นหรือไม่ ฉันไม่สามารถทำให้เหตุการณ์รูปแบบเริ่มทำงานเป็นอย่างอื่นได้
SomethingBetter

ปัญหาเดียวที่นี่คือคุณไม่สามารถจัดเรียงรายการตามตรรกะของคุณได้
GorillaApe

นี่เป็นทางออกที่ยอดเยี่ยม ฉันต้องการให้มันทำงานร่วมกับDataGridComboBoxColumnแม้ว่า มีโอกาสแก้ไหม ฉันไม่สามารถหาวิธีเข้าถึงComboBoxไฟล์DataGridComboBoxColumn.
Soko

46

อย่า! Enums เป็นวัตถุดั้งเดิมและไม่ใช่ออบเจ็กต์ UI ทำให้รองรับ UI ใน. ToString () จะค่อนข้างแย่จากมุมมองการออกแบบ คุณกำลังพยายามแก้ไขปัญหาที่ไม่ถูกต้องที่นี่ปัญหาที่แท้จริงคือคุณไม่ต้องการให้ Enum ToString () ปรากฏในกล่องคำสั่งผสม!

ตอนนี้เป็นปัญหาที่แก้ได้แน่นอน! คุณสร้างวัตถุ UI เพื่อแสดงรายการกล่องคำสั่งผสมของคุณ:

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

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

จากนั้นเพิ่มอินสแตนซ์ของคลาสนี้ในคอลเลกชันรายการของกล่องคำสั่งผสมและตั้งค่าคุณสมบัติเหล่านี้

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

1
ฉันเห็นด้วยสุดใจ คุณไม่ควรเปิดเผยผลลัพธ์ของ ToString () กับ UI และคุณไม่ได้รับการแปล
Øyvind Skaar

ฉันรู้ว่ามันเก่า แต่มันแตกต่างกันอย่างไร?
nportelli

2
ผมเคยเห็นที่คล้ายกันแก้ปัญหาที่แทนการใช้ชั้นเองที่พวกเขาแมปค่า enum ไปยังDictionaryและใช้KeyและValueคุณสมบัติเป็นและDisplayMember ValueMember
Jeff B

42

TypeConverter ฉันคิดว่านี่คือสิ่งที่ฉันกำลังมองหา ทุกคนทักทายSimon Svensson !

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

สิ่งที่ฉันต้องเปลี่ยนใน enum ปัจจุบันของฉันคือเพิ่มบรรทัดนี้ก่อนการประกาศ

[TypeConverter(typeof(EnumToStringUsingDescription))]

เมื่อฉันทำเช่นนั้น enum ใด ๆ จะแสดงโดยใช้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 ไม่ได้ถูกผูกไว้กับการอธิบายแบบคงที่ผู้ปกปิดสามารถเติมค่าจากแหล่งอื่นที่ไม่ใช่แอตทริบิวต์
Dmitry Tashkinov

3
ดึงผมมาสองสามชั่วโมงแล้ว แต่ก็ยังใช้งานไม่ได้แม้ในแอปคอนโซลธรรมดา ฉันตกแต่ง enum ด้วย[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

33

ใช้ตัวอย่างการแจงนับของคุณ:

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
อนันดา

8

คุณสามารถสร้างโครงสร้างทั่วไปที่คุณสามารถใช้กับ enums ทั้งหมดของคุณที่มีคำอธิบายได้ ด้วยการแปลงโดยนัยไปยังและจากคลาสตัวแปรของคุณยังคงทำงานเหมือน enum ยกเว้นวิธี 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สิ่งนี้ไม่เคารพสิ่งนี้สำหรับ enums

คุณสามารถเชื่อมโยงกับstring[]คำอธิบายหรือสิ่งที่เป็นหลักเช่นคู่คีย์ / ค่า? (desription / value) - สิ่งที่ชอบ:

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เป็นประเภทของEnumWrapper<T>แทนที่จะเป็นTตัวมันเอง Reading (HowNice)myComboBox.selectedItem will return the selected value as the enum value.ผมคิดว่าต้องการ scraimer
Peter Lee

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 ที่สวยงามจะปรากฏขึ้นและความจริงที่ว่าไม่มีใครสามารถสร้างอินสแตนซ์ของคลาสของคุณได้อีกต่อไปโดยพื้นฐานแล้วจะทำให้เป็น enum

ps อาจต้องมีการแก้ไขไวยากรณ์เล็กน้อยฉันไม่ค่อยดีกับ C # (ผู้ชายชวา)


1
วิธีนี้ช่วยแก้ปัญหา Combobox ได้อย่างไร?
peSHIr

ทีนี้เมื่อใส่วัตถุใหม่ลงในคอมโบบ็อกซ์ ToString ของมันจะแสดงอย่างถูกต้องและคลาสยังคงทำหน้าที่เหมือน enum
jjnguy

1
คงจะเป็นคำตอบของฉันเช่นกัน
Mikko Rantanen

3
และดูว่าผู้โพสต์ต้นฉบับไม่ต้องการชั้นเรียนอย่างชัดเจน ฉันไม่คิดว่าชั้นเรียนจะทำงานได้มากกว่านี้ คุณสามารถสรุปคำอธิบายและ ToString แทนที่ไปยังคลาสพาเรนต์สำหรับ enums ทั้งหมด หลังจากนี้สิ่งที่คุณต้องมีคือตัวสร้างprivate HowNice(String desc) : base(desc) { }และฟิลด์แบบคงที่
Mikko Rantanen

ฉันหวังว่าจะหลีกเลี่ยงสิ่งนี้เนื่องจากมันหมายความว่าการแจงนับแต่ละครั้งที่ฉันทำจะต้องใช้คลาสของตัวเอง ฮึ.
Shalom Craimer

3

เนื่องจากคุณไม่ต้องการสร้างคลาสสำหรับแต่ละ enum ฉันขอแนะนำให้สร้างพจนานุกรมของค่า enum / ข้อความที่แสดงและเชื่อมโยงแทน

โปรดทราบว่าสิ่งนี้มีการพึ่งพาเมธอด 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;
}

ความคิดดี. แต่ฉันจะใช้มันกับคอมโบบ็อกซ์ได้อย่างไร? เมื่อผู้ใช้เลือกรายการจากคอมโบบ็อกซ์ฉันจะรู้ได้อย่างไรว่ารายการใดที่เขาเลือก ค้นหาโดยสตริงคำอธิบาย? ที่ทำให้ผิวของฉันคัน ... (อาจมีสตริง "ชนกัน" ระหว่างสตริงคำอธิบาย)
Shalom Craimer

คีย์ของรายการที่เลือกจะเป็นค่า enum จริง นอกจากนี้อย่าชนสตริงคำอธิบาย - ผู้ใช้จะบอกความแตกต่างได้อย่างไร
Richard Szalay

<cont> หากคุณมีสตริงคำอธิบายที่ชนกันคุณก็ไม่ควรผูกค่าของ enum กับคอมโบบ็อกซ์โดยตรง
Richard Szalay

อืม ... คุณช่วยยกตัวอย่างโค้ดเกี่ยวกับวิธีเพิ่มไอเท็มในคอมโบบ็อกซ์ได้ไหม ทั้งหมดที่ฉันคิดได้คือ "foreach (string s ในคำอธิบาย Dict.Values) {this.comboboxItems.Add (s);}"
Shalom Craimer

1
ComboBox.DataSource = พจนานุกรม;
Richard Szalay

3

ไม่สามารถแทนที่ ToString () ของ enums ใน C # ได้ อย่างไรก็ตามคุณสามารถใช้วิธีการขยาย

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

แน่นอนว่าคุณจะต้องทำการเรียกใช้เมธอดอย่างชัดเจนนั่นคือ

HowNice.ReallyNice.ToString(0)

นี่ไม่ใช่วิธีแก้ปัญหาที่ดีด้วยคำสั่ง switch และทั้งหมด - แต่ควรใช้งานได้และหวังว่าจะเขียนซ้ำหลาย ๆ ครั้ง ...


โปรดทราบว่าการควบคุมที่ผูกกับ enum ของคุณจะไม่เรียกวิธีการส่วนขยายนี้ แต่จะเรียกการใช้งานเริ่มต้น
Richard Szalay

ขวา. ดังนั้นนี่จึงเป็นตัวเลือกที่ใช้ได้หากคุณต้องการคำอธิบายที่ไหนสักแห่งมันไม่ได้ช่วยแก้ปัญหาคอมโบบ็อกซ์ที่เกิดขึ้น
peSHIr

ปัญหาที่ใหญ่กว่าคือสิ่งนี้จะไม่ถูกเรียกใช้ (เป็นวิธีการขยาย) - วิธีการของอินสแตนซ์ที่มีอยู่แล้วจะให้ความสำคัญเสมอ
Marc Gravell

แน่นอนว่ามาร์คพูดถูก (เช่นเคย?) ประสบการณ์. 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
}

นอกจากนี้คุณสามารถเพิ่ม "วิธีการโรงงาน" แบบคงที่เพื่อสร้างรายการคอมโบบ็อกซ์ตามประเภท enum (ค่อนข้างเหมือนกับวิธี GetDescriptions ที่คุณมี) สิ่งนี้จะช่วยให้คุณไม่ต้องใช้เอนทิตีหนึ่งรายการต่อ enum แต่ละประเภทและยังมีสถานที่ที่ดี / มีเหตุผลสำหรับเมธอดตัวช่วย "GetDescriptions" (โดยส่วนตัวฉันจะเรียกมันว่า FromEnum (T obj) ...


1

สร้างคอลเลกชันที่มีสิ่งที่คุณต้องการ (เช่นออบเจ็กต์ธรรมดาที่มีValueคุณสมบัติที่มีHowNiceค่า enum และ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; } }
    }

}

อย่างที่คุณเห็นคอลเลกชันนี้สามารถปรับแต่งได้อย่างง่ายดายด้วยแลมบ์ดาเพื่อเลือกชุดย่อยของตัวแจงนับของคุณและ / หรือใช้การจัดรูปแบบที่กำหนดเองstringแทนการใช้GetDescription<T>(x)ฟังก์ชันที่คุณพูดถึง


ยอดเยี่ยม แต่ฉันกำลังมองหาสิ่งที่ต้องการการทำงานน้อยลงในโค้ด
Shalom Craimer

แม้ว่าคุณจะสามารถใช้คอลเลกชันทั่วไปเดียวกันสำหรับสิ่งนี้สำหรับตัวนับทั้งหมดของคุณ? ฉันไม่ได้แนะนำให้เขียนคอลเลกชันแบบกำหนดเองสำหรับแต่ละ enum แน่นอน
peSHIr

1

คุณสามารถใช้ PostSharp เพื่อกำหนดเป้าหมาย Enum ToString และเพิ่มโค้ด aditionall ที่คุณต้องการ สิ่งนี้ไม่ต้องการการเปลี่ยนแปลงรหัสใด ๆ


1

สิ่งที่คุณต้องการคือเปลี่ยน enum ให้เป็น ReadonlyCollection และผูกคอลเลกชันเข้ากับ combobox (หรือคู่คีย์ - ค่าใด ๆ ที่เปิดใช้งานการควบคุมสำหรับเรื่องนั้น)

ก่อนอื่นคุณต้องมีชั้นเรียนเพื่อบรรจุรายการของรายการ เนื่องจากสิ่งที่คุณต้องการคือคู่ 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
}

นี่คืออินเทอร์เฟซและคลาสตัวอย่างที่ใช้งานได้โปรดสังเกตว่าคีย์คลาส 'ถูกพิมพ์ลงใน Enum อย่างมากและมีการใช้คุณสมบัติ IValueDesc คู่คีย์ / ค่า

ตอนนี้คลาส 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 ดังนั้นค่า combobox ควรส่งคืนค่า enum หากคุณเชื่อมโยงกับ proprtie ที่เหมาะสม

ฉันได้เพิ่มคุณสมบัติ T this [Enum t] เพื่อให้คอลเลกชันมีประโยชน์มากยิ่งขึ้นกว่าการใช้งานคอมโบธรรมดา ๆ ตัวอย่างเช่น textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;

แน่นอนคุณสามารถเลือกที่จะเปลี่ยน MyItem ให้เป็นคลาสคีย์ / ค่าที่ใช้สำหรับ puprose นี้เท่านั้นโดยข้าม MyItem ในอาร์กิวเมนต์ประเภท EnumToReadnlyCollection ทั้งหมด แต่คุณจะถูกบังคับให้ใช้ int สำหรับคีย์ (หมายถึงการรับ combobox1.SelectedValue จะคืนค่า int และไม่ใช่ประเภท enum) คุณสามารถหลีกเลี่ยงสิ่งนั้นได้หากคุณสร้างคลาส KeyValueItem เพื่อแทนที่ MyItem และอื่น ๆ ...


1

ขออภัยที่เอากระทู้เก่านี้ขึ้น

ฉันจะใช้วิธีต่อไปนี้เพื่อแปล enum เนื่องจากสามารถแสดงค่าที่มีความหมายและแปลให้กับผู้ใช้ไม่ใช่แค่คำอธิบายผ่านช่องข้อความแบบหล่นลงในตัวอย่างนี้

ก่อนอื่นฉันสร้างเมธอดง่ายๆที่เรียกว่า OwToStringByCulture เพื่อรับสตริงที่แปลเป็นภาษาท้องถิ่นจากไฟล์ทรัพยากรส่วนกลางในตัวอย่างนี้คือ BiBongNet.resx ในโฟลเดอร์ App_GlobalResources ภายในไฟล์ทรัพยากรนี้ตรวจสอบให้แน่ใจว่าคุณมีสตริงทั้งหมดเหมือนกับค่าของ enum (ReallyNice, SortOfNice, NotNice) ในวิธีนี้ฉันส่งพารามิเตอร์: resourceClassName ซึ่งโดยปกติจะเป็นชื่อของไฟล์ทรัพยากร

ต่อไปฉันสร้างวิธีการแบบคงที่เพื่อเติมรายการแบบเลื่อนลงโดยมี enum เป็นแหล่งข้อมูลที่เรียกว่า OwFillDataWithEnum วิธีนี้สามารถใช้กับ enum ใดก็ได้ในภายหลัง

จากนั้นในหน้าที่มีรายการแบบเลื่อนลงที่เรียกว่า DropDownList1 ฉันตั้งค่าใน Page_Load โค้ดง่ายๆเพียงบรรทัดเดียวต่อไปนี้เพื่อเติม enum ลงในรายการแบบเลื่อนลง

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

แค่นั้นแหละ. ฉันคิดว่าด้วยวิธีการง่ายๆเช่นนี้คุณสามารถเติมการควบคุมรายการใด ๆ ด้วย enum ใดก็ได้โดยไม่เพียง แต่เป็นค่าที่อธิบายได้ แต่ยังแสดงข้อความที่แปล คุณสามารถทำให้วิธีการทั้งหมดนี้เป็นวิธีการขยายเพื่อให้ใช้งานได้ดีขึ้น

หวังว่าจะช่วยได้ แชร์เพื่อรับการแบ่งปัน!

นี่คือวิธีการ:

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

รหัสง่ายและถอดรหัสอย่างรวดเร็ว


ตัวแปรสตริงควรเป็นแบบคงที่และประกาศดังนี้: Static String [] strings = new [] {... }
Sérgio

ปัญหาเดียวของเรื่องนี้คือคุณจะต้องมีฟังก์ชันสำหรับการแจงนับทุกครั้งและคำอธิบายจะแยกออกจากการแจงนับเอง ...
Avi Turner

1

ฉันลองใช้วิธีนี้และได้ผลสำหรับฉัน

ฉันสร้างคลาส wrapper สำหรับ enums และโอเวอร์โหลดตัวดำเนินการโดยนัยเพื่อที่ฉันจะสามารถกำหนดให้กับตัวแปร enum ได้ (ในกรณีของฉันฉันต้องผูกอ็อบเจกต์กับComboBoxค่า)

คุณสามารถใช้การสะท้อนเพื่อจัดรูปแบบค่า enum ตามที่คุณต้องการในกรณีของฉันฉันดึงDisplayAttributeค่า enum ออกมา (ถ้ามีอยู่)

หวังว่านี่จะช่วยได้

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´ ประกาศอยู่ที่ไหน?
กลัว

1
คำตอบจาก @scraimer เหมือนกันยกเว้นว่าเขาใช้แอตทริบิวต์นอกกรอบในขณะที่คุณใช้แอตทริบิวต์ที่กำหนดเองบางประเภท
Oliver
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.