Làm cách nào để có một combobox enum ràng buộc với định dạng chuỗi tùy chỉnh cho các giá trị enum?


135

Trong bài Enum ToString , một phương thức được mô tả để sử dụng thuộc tính tùy chỉnh DescriptionAttributenhư thế này:

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

Và sau đó, bạn gọi một hàm GetDescription, sử dụng cú pháp như:

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

Nhưng điều đó không thực sự giúp tôi khi tôi chỉ muốn tạo một ComboBox với các giá trị của một enum, vì tôi không thể buộc ComboBox gọiGetDescription .

Những gì tôi muốn có các yêu cầu sau:

  • Đọc (HowNice)myComboBox.selectedItemsẽ trả về giá trị được chọn là giá trị enum.
  • Người dùng sẽ thấy các chuỗi hiển thị thân thiện với người dùng và không chỉ là tên của các giá trị liệt kê. Vì vậy, thay vì nhìn thấy " NotNice", người dùng sẽ thấy " Not Nice At All".
  • Hy vọng rằng, giải pháp sẽ yêu cầu thay đổi mã tối thiểu cho các liệt kê hiện có.

Rõ ràng, tôi có thể triển khai một lớp mới cho mỗi enum mà tôi tạo và ghi đè lên nó ToString(), nhưng đó là rất nhiều công việc cho mỗi enum, và tôi muốn tránh điều đó.

Có ý kiến ​​gì không?

Heck, tôi thậm chí sẽ ôm một cái ôm như một tiền thưởng :-)


1
jjnguy là chính xác rằng các enum Java giải quyết điều này một cách độc đáo ( javahowto.blogspot.com/2006/10/ mẹo ), nhưng đó là sự liên quan đáng ngờ.
Matthew Flaschen

8
Java Enums là một trò đùa. Có thể họ sẽ thêm Thuộc tính vào năm 2020: /
Chad Grant

Đối với một giải pháp nhẹ hơn (nhưng được cho là kém mạnh mẽ hơn) xem chủ đề của tôi .
Gutblender

Câu trả lời:


42

Bạn có thể viết TypeConverter đọc các thuộc tính được chỉ định để tra cứu chúng trong tài nguyên của bạn. Do đó, bạn sẽ nhận được hỗ trợ đa ngôn ngữ cho tên hiển thị mà không gặp nhiều rắc rối.

Xem xét các phương thức ConvertFrom / ConvertTo của TypeConverter và sử dụng sự phản chiếu để đọc các thuộc tính trên các trường enum của bạn .


OK, tôi đã viết một số mã (xem câu trả lời của tôi cho câu hỏi này) - bạn có nghĩ thế là đủ, tôi có thiếu thứ gì không?
Shalom Craimer

1
Đẹp một. Phần mềm tốt hơn, nhưng có thể là quá mức cần thiết cho phần mềm chạy không ngừng của bạn sẽ không bị toàn cầu hóa theo bất kỳ cách nào. (Tôi biết, giả định đó sẽ trở thành sai sau này.;))
peSHIr

85

ComboBoxcó mọi thứ bạn cần: thuộc FormattingEnabledtính mà bạn nên đặt trueFormatsự kiện, nơi bạn sẽ cần đặt logic định dạng mong muốn. Như vậy

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

Điều này chỉ làm việc với combobox databound? Tôi không thể lấy sự kiện Format để bắn khác.
SomethingBetter

vấn đề duy nhất ở đây là bạn không thể sắp xếp danh sách theo logic của mình
GorillaApe

Đây là một giải pháp tuyệt vời. Tôi sẽ cần nó để làm việc với một DataGridComboBoxColumnmặc dù. Bất kỳ cơ hội để giải quyết nó? Tôi không thể tìm thấy một cách để có được quyền truy cập vào các ComboBoxcủa DataGridComboBoxColumn.
Soko

46

Đừng! Enums là nguyên thủy và không phải là đối tượng UI - khiến chúng phục vụ UI trong .ToString () sẽ khá tệ từ quan điểm thiết kế. Bạn đang cố gắng giải quyết vấn đề sai ở đây: vấn đề thực sự là bạn không muốn Enum.ToString () hiển thị trong hộp tổ hợp!

Bây giờ đây là một vấn đề rất dễ giải quyết thực sự! Bạn tạo một đối tượng UI để thể hiện các mục hộp tổ hợp của mình:

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

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

Và sau đó chỉ cần thêm các thể hiện của lớp này vào bộ sưu tập Mục của hộp tổ hợp của bạn và đặt các thuộc tính này:

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

1
Tôi đồng ý hết lòng. Bạn cũng không nên để lộ kết quả của ToString () cho UI. Và, bạn không nhận được nội địa hóa.
yvind Skaar

Tôi biết điều này đã cũ, nhưng nó khác nhau như thế nào?
nportelli

2
Tôi đã nhìn thấy một tương tự giải pháp nơi thay vì sử dụng một lớp tùy chỉnh, họ ánh xạ các giá trị enum để một Dictionaryvà sử dụng KeyValuetính chất như DisplayMemberValueMember.
Jeff B

42

KiểuConverter. Tôi nghĩ rằng đây là những gì tôi đang tìm kiếm. Tất cả mưa đá Simon Svensson !

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

Tất cả tôi cần thay đổi trong enum hiện tại của tôi là thêm dòng này trước khi khai báo của họ.

[TypeConverter(typeof(EnumToStringUsingDescription))]

Khi tôi làm điều đó, mọi enum sẽ được hiển thị bằng cách sử dụng các trường DescriptionAttributecủa nó.

Ồ, và TypeConvertersẽ được định nghĩa như thế này:

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

Điều này giúp tôi với trường hợp ComboBox của mình, nhưng rõ ràng không thực sự ghi đè lên ToString(). Tôi đoán tôi sẽ giải quyết trong lúc này ...


3
Bạn đang xử lý Enum -> String, nhưng bạn cũng sẽ cần Enum> InstanceDescriptor và String -> Enum nếu bạn muốn thực hiện hoàn chỉnh. Nhưng tôi đoán hiển thị nó là đủ cho nhu cầu của bạn tại thời điểm này. ;)
sisve

1
Giải pháp này chỉ hoạt động khi mô tả của bạn là tĩnh, không may.
Llyle

Nhân tiện, việc sử dụng TypeConverter không bị ràng buộc với các phân đoạn tĩnh, trình bao có thể điền các giá trị từ các nguồn khác ngoài các thuộc tính.
Dmitry Tashkinov

3
Đã kéo tóc tôi vài giờ rồi, nhưng dường như nó vẫn không hoạt động ngay cả trong các ứng dụng bảng điều khiển đơn giản. Tôi trang trí enum với [TypeConverter(typeof(EnumToStringUsingDescription))] public enum MyEnum {[Description("Blah")] One}, thử làm Console.WriteLine(MyEnum.One)và nó vẫn xuất hiện dưới dạng "Một". Bạn có cần một số phép thuật đặc biệt như TypeDescriptor.GetConverter(typeof(MyEnum)).ConvertToString(MyEnum.One)(hoạt động tốt) không?
Dav

1
@scraimer Tôi đã đăng một phiên bản cờ hỗ trợ mã của bạn. tất cả các quyền dành cho bạn ...
Avi Turner

32

Sử dụng ví dụ liệt kê của bạn:

using System.ComponentModel;

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

Tạo tiện ích mở rộng:

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

Sau đó, bạn có thể sử dụng một cái gì đó như sau:

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

Xem: http://www.blackwasp.co.uk/EnumDescrip.aspx để biết thêm thông tin. Tín dụng cho Richrd Carr cho giải pháp


Tôi đã theo dõi các chi tiết tại trang web được giới thiệu và sử dụng nó như sau, có vẻ đơn giản đối với tôi 'chuỗi myDesc = HowNice.ReallyNice.Descrip ();' myDesc sẽ xuất ra thực sự tốt đẹp
Ananda

8

Bạn có thể tạo một cấu trúc chung mà bạn có thể sử dụng cho tất cả các enum có mô tả. Với các chuyển đổi ngầm định đến và từ lớp, các biến của bạn vẫn hoạt động như enum ngoại trừ phương thức 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;
    }

}

Ví dụ sử dụng:

Described<HowNice> nice = HowNice.ReallyNice;

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

5

Tôi không nghĩ bạn có thể làm điều đó mà không đơn giản ràng buộc với một loại khác - ít nhất, không thuận tiện. Thông thường, ngay cả khi bạn không thể kiểm soát ToString(), bạn có thể sử dụng TypeConverterđịnh dạng tùy chỉnh để thực hiện - nhưng IIRC System.ComponentModelcông cụ không tôn trọng điều này đối với enums.

Bạn có thể liên kết với một string[]trong các mô tả hoặc một cái gì đó về cơ bản giống như cặp khóa / giá trị? (desription / value) - đại loại như:

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

Và sau đó liên kết với EnumWrapper<HowNice>.GetValues()


1
Tên 'GetDes mô tả' không tồn tại trong bối cảnh hiện tại. tôi đang sử dụng .NET 4.0
Muhammad Adeel Zahid

@MuhammadAdeelZahid nhìn kỹ vào phần đầu của câu hỏi - xuất phát từ bài đăng được liên kết: stackoverflow.com/questions/479410/enum-tostring
Marc Gravell

xin lỗi nhưng không thể nhận được bất kỳ manh mối từ câu hỏi. phương pháp của bạn không được biên dịch và hiển thị lỗi.
Muhammad Adeel Zahid

Xin chào Marc, tôi đã thử ý tưởng của bạn. Nó hoạt động, nhưng theComboBox.SelectItemlà loại EnumWrapper<T>, thay vì Tchính nó. Tôi nghĩ rằng scraimer muốn Reading (HowNice)myComboBox.selectedItem will return the selected value as the enum value..
Peter Lee

5

Cách tốt nhất để làm điều này là tạo ra một lớp học.

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

Tôi tin rằng đó là cách tốt nhất để làm điều đó.

Khi được nhồi trong các hộp tổ hợp, ToString đẹp sẽ được hiển thị và thực tế là không ai có thể tạo thêm bất kỳ trường hợp nào trong lớp của bạn về cơ bản làm cho nó trở thành một enum.

ps có thể cần phải sửa một số cú pháp nhỏ, tôi không giỏi lắm với C #. (Anh chàng Java)


1
Làm thế nào điều này giúp với vấn đề combobox?
peSHIr

Chà, bây giờ, khi đối tượng mới được đặt trong một hộp tổ hợp, ToString của nó sẽ được hiển thị đúng và lớp vẫn hoạt động như một enum.
jjnguy

1
Cũng sẽ là câu trả lời của tôi.
Mikko Rantanen

3
Và xem làm thế nào các poster ban đầu rõ ràng không muốn một lớp. Tôi không nghĩ rằng một lớp học là nhiều công việc hơn. Bạn có thể trừu tượng mô tả và ToString ghi đè lên một lớp cha cho tất cả các enum. Sau đó, tất cả những gì bạn cần là một hàm tạo private HowNice(String desc) : base(desc) { }và các trường tĩnh.
Mikko Rantanen

Tôi đã hy vọng tránh điều này, vì điều đó có nghĩa là mỗi phép liệt kê tôi thực hiện sẽ yêu cầu lớp học riêng của nó. Ừ
Shalom Craimer

3

Cho rằng bạn không muốn tạo một lớp cho mỗi enum, tôi khuyên bạn nên tạo một từ điển của giá trị enum / văn bản hiển thị và thay vào đó ràng buộc.

Lưu ý rằng điều này có sự phụ thuộc vào các phương thức phương thức GetDes mô tả trong bài viết gốc.

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

Ý kiến ​​hay. Nhưng làm thế nào tôi có thể sử dụng cái này với combobox? Khi người dùng chọn một mục từ hộp tổ hợp, làm thế nào để tôi biết mục nào mình đã chọn? Tìm kiếm theo chuỗi Mô tả? Điều đó làm cho da tôi bị ngứa ... (có thể có một chuỗi "va chạm" giữa các chuỗi Mô tả)
Shalom Craimer

Khóa của mục được chọn sẽ là giá trị enum thực tế. Ngoài ra, không va chạm các chuỗi mô tả - người dùng sẽ cho biết sự khác biệt như thế nào?
Richard Szalay

<cont> nếu bạn có các chuỗi mô tả va chạm, thì bạn không nên liên kết trực tiếp các giá trị của enum với một hộp tổ hợp.
Richard Szalay

hmmm ... Chà, bạn có thể cho tôi mã ví dụ về cách bạn sẽ thêm các mục vào combobox không? Tất cả những gì tôi có thể nghĩ là "foreach (chuỗi s trong mô tảDict.Values) {this.combobox.Items.Add (s);}"
Shalom Craimer

1
ComboBox.DataSource = từ điển;
Richard Szalay

3

Không thể ghi đè ToString () của enum trong C #. Tuy nhiên, bạn có thể sử dụng các phương pháp mở rộng;

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

Tất nhiên bạn sẽ phải thực hiện một cuộc gọi rõ ràng đến phương thức, nghĩa là;

HowNice.ReallyNice.ToString(0)

Đây không phải là một giải pháp tốt, với một câu lệnh chuyển đổi và tất cả - nhưng nó sẽ hoạt động và hy vọng sẽ được viết lại cho nhiều người viết lại ...


Xin lưu ý rằng điều khiển liên kết với enum của bạn sẽ không gọi phương thức mở rộng này, nó sẽ gọi việc thực hiện mặc định.
Richard Szalay

Đúng. Vì vậy, đây là một tùy chọn khả thi nếu bạn cần một mô tả ở đâu đó, nó không giúp ích gì cho vấn đề combobox được đặt ra.
peSHIr

Một vấn đề lớn hơn là điều này sẽ không bao giờ được gọi (như một phương thức mở rộng) - các phương thức thể hiện đã luôn được ưu tiên.
Marc Gravell

Tất nhiên Marc đúng (như mọi khi?). Trải nghiệm .NET của tôi là tối thiểu, nhưng việc cung cấp một tham số giả cho phương thức nên thực hiện thủ thuật. Chỉnh sửa câu trả lời.
Bjorn

2

Theo dõi câu trả lời @scraimer, đây là phiên bản của trình chuyển đổi kiểu enum-to-string, cũng hỗ trợ các cờ:

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

}

Và một phương pháp mở rộng để sử dụng nó:

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

Tôi sẽ viết một lớp chung để sử dụng với bất kỳ loại nào. Tôi đã từng sử dụng một cái gì đó như thế này trong quá khứ:

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
}

Trên hết, bạn có thể thêm một "phương thức xuất xưởng" tĩnh để tạo danh sách các mục combobox được cung cấp một loại enum (khá giống với phương thức GetDes mô tả bạn có ở đó). Điều này sẽ giúp bạn tiết kiệm được việc phải triển khai một thực thể cho mỗi loại enum và cũng cung cấp một vị trí hợp lý / hợp lý cho phương thức trợ giúp "GetDes mô tả" (cá nhân tôi sẽ gọi nó là FromEnum (T obj) ...


1

Tạo một bộ sưu tập chứa những gì bạn cần (như các đối tượng đơn giản chứa thuộc Valuetính chứa HowNicegiá trị enum và thuộc Descriptiontính chứa GetDescription<HowNice>(Value)và cơ sở dữ liệu kết hợp với bộ sưu tập đó.

Bit như thế này:

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

khi bạn có một lớp sưu tập như thế này:

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

}

Như bạn có thể thấy, bộ sưu tập này có thể dễ dàng tùy chỉnh với lambda để chọn một tập hợp con của trình liệt kê của bạn và / hoặc thực hiện định dạng tùy chỉnh stringthay vì sử dụng GetDescription<T>(x)chức năng bạn đề cập.


Tuyệt vời, nhưng tôi đang tìm kiếm một cái gì đó đòi hỏi công việc ít hơn trong mã.
Shalom Craimer

Ngay cả khi bạn có thể sử dụng cùng một bộ sưu tập chung cho loại điều này cho tất cả các điều tra viên của bạn? Tôi đã không đề xuất viết tùy chỉnh một bộ sưu tập như vậy cho mỗi enum, tất nhiên.
peSHIr

1

Bạn có thể sử dụng PostSharp để nhắm mục tiêu Enum.ToString và thêm mã aditionall bạn muốn. Điều này không yêu cầu bất kỳ thay đổi mã.


1

Những gì bạn cần là biến enum thành ReadonlyCollection và liên kết bộ sưu tập với hộp tổ hợp (hoặc bất kỳ điều khiển cặp khóa-giá trị nào được kích hoạt cho vấn đề đó)

Trước hết, bạn cần một lớp để chứa các mục của danh sách. Vì tất cả những gì bạn cần là cặp int / chuỗi tôi đề nghị sử dụng giao diện và kết hợp lớp cơ sở để bạn có thể triển khai chức năng trong bất kỳ đối tượng nào bạn muốn:

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
}

Đây là giao diện và một lớp mẫu thực hiện nó. Chú ý rằng Khóa của lớp được gõ mạnh vào Enum và các dự án IValueDescrecItem được triển khai rõ ràng (vì vậy lớp có thể có bất kỳ thuộc tính nào và bạn có thể CHỌN những thuộc tính đó thực hiện Cặp khóa / giá trị.

Bây giờ là lớp 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)];
        }
    }

}

Vì vậy, tất cả những gì bạn cần trong mã của bạn là:

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

Hãy nhớ rằng bộ sưu tập của bạn được nhập bằng MyItem để giá trị combobox sẽ trả về giá trị enum nếu bạn liên kết với proprtie thích hợp.

Tôi đã thêm thuộc tính T [Enum t] này để làm cho bộ sưu tập thậm chí còn hữu ích hơn một bộ kết hợp đơn giản có thể tiêu thụ, ví dụ textBox1.Text = enumcol [HowNice.ReallyNice] .NicuityDes mô tả;

Tất nhiên, bạn có thể chọn biến MyItem thành một lớp Key / Value chỉ được sử dụng cho con rối này một cách hiệu quả bỏ qua MyItem trong các đối số kiểu của EnumToReadnlyCollection hoàn toàn, nhưng sau đó bạn buộc phải đi với int cho khóa (có nghĩa là nhận được combobox1.SelectedValue sẽ trả về int và không phải kiểu enum). Bạn giải quyết vấn đề đó nếu bạn tạo một lớp KeyValueItem để thay thế MyItem và cứ thế ...


1

Xin lỗi vì nhận được chủ đề cũ này lên.

Tôi sẽ đi theo cách sau để bản địa hóa enum, vì nó có thể hiển thị các giá trị có ý nghĩa và được bản địa hóa cho người dùng, không chỉ mô tả, thông qua trường văn bản danh sách thả xuống trong ví dụ này.

Đầu tiên, tôi tạo một phương thức đơn giản gọi là OwToStringByCARM để lấy các chuỗi được bản địa hóa từ tệp tài nguyên toàn cầu, trong ví dụ này là BiBongNet.resx trong thư mục App_GlobalResource. Trong tệp tài nguyên này, đảm bảo bạn có tất cả các chuỗi giống như các giá trị của enum (reallyNice, SortOfNice, NotNice). Trong phương thức này, tôi truyền vào tham số: resourceClassName thường là tên của tệp tài nguyên.

Tiếp theo, tôi tạo một phương thức tĩnh để điền vào danh sách thả xuống với enum làm nguồn dữ liệu của nó, được gọi là OwFillDataWithEnum. Phương pháp này có thể được sử dụng với bất kỳ enum nào sau này.

Sau đó, trong trang có danh sách thả xuống có tên DropDownList1, tôi đã đặt trong Page_Load sau đây chỉ một dòng mã đơn giản để điền enum vào danh sách thả xuống.

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

Đó là nó. Tôi nghĩ với một số phương pháp đơn giản như thế này, bạn có thể điền bất kỳ điều khiển danh sách nào với bất kỳ enum nào, không chỉ là các giá trị mô tả mà còn là văn bản được bản địa hóa để hiển thị. Bạn có thể thực hiện tất cả các phương thức này như các phương thức mở rộng để sử dụng tốt hơn.

Hy vọng điều này giúp đỡ. Chia sẻ để được chia sẻ!

Dưới đây là các phương pháp:

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
}

Để giải quyết vấn đề này, bạn nên sử dụng Phương thức mở rộng và Mảng chuỗi như vậy:

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

Mã đơn giản và giải mã nhanh.


biến chuỗi phải là Tĩnh và được khai báo như vậy: Chuỗi tĩnh [] chuỗi = new [] {...}
Sérgio

Vấn đề duy nhất với điều này là bạn sẽ cần phải có một hàm cho mỗi phép liệt kê và phần mô tả sẽ tách rời khỏi bảng liệt kê ...
Avi Turner

1

Tôi đã thử phương pháp này và nó hiệu quả với tôi.

Tôi đã tạo một lớp bao bọc cho enums và nạp chồng toán tử ẩn để tôi có thể gán nó cho các biến enum (trong trường hợp của tôi, tôi phải liên kết một đối tượng với một ComboBox giá trị).

Bạn có thể sử dụng sự phản chiếu để định dạng các giá trị enum theo cách bạn muốn, trong trường hợp của tôi, tôi lấy DisplayAttribute ra các giá trị enum (nếu tồn tại).

Hi vọng điêu nay co ich.

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

BIÊN TẬP:

Chỉ trong trường hợp, tôi sử dụng các chức năng sau đây để có được những enumgiá trị mà tôi sử dụng cho DataSourcecácComboBox

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

Khi bạn có GetDescriptionphương thức (nó cần phải là tĩnh toàn cục), bạn có thể sử dụng phương thức này thông qua một phương thức mở rộng:

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
Chào mừng bạn đến với stackoverflow! Luôn luôn tốt hơn để cung cấp một mô tả ngắn cho mã mẫu để cải thiện độ chính xác của bài đăng :)
Phần mềm Picrofo

-1

Bạn có thể định nghĩa Enum là

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

và sau đó sử dụng HowNice.GetStringValue().


2
Điều này không biên dịch (Tôi có .NET 3.5). Trường hợp được khai báo ở đâu?
kinh ngạc

1
Câu trả lời từ @scraimer là như nhau, ngoại trừ việc anh ta đang sử dụng một thuộc tính ngoài khung, trong khi bạn sử dụng một số loại thuộc tính tự xác định.
Oliver
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.