2009-08-25 33 views
19

Tôi có một liệt kê nhưLàm thế nào để có tên người dùng cho các bảng liệt kê?

Enum Complexity 
{ 
    NotSoComplex, 
    LittleComplex, 
    Complex, 
    VeryComplex 
} 

Và tôi muốn sử dụng nó trong một danh sách thả xuống, nhưng không muốn nhìn thấy tên Camel như vậy trong danh sách (trông thực sự kỳ lạ cho người sử dụng). Thay vào đó, tôi muốn có từ ngữ bình thường, như Không quá phức tạp Ít phức tạp (v.v ..)

Ngoài ra, ứng dụng của tôi là đa ngôn ngữ và tôi muốn hiển thị các chuỗi được bản địa hóa và tôi sử dụng helper, TranslationHelper (chuỗi strID) cung cấp cho tôi phiên bản được bản địa hóa cho một id chuỗi.

Tôi có một giải pháp làm việc, nhưng không phải là rất tao nhã: tôi tạo ra một lớp helper cho enum, với một phức tạp thành viên và ToString() ghi đè, như dưới đây (mã đơn giản hóa)

public class ComplexityHelper 
{ 
    public ComplexityHelper(Complexity c, string desc) 
    { m_complex = c; m_desc=desc; } 

    public Complexity Complexity { get { ... } set {...} } 
    public override ToString() { return m_desc; } 

    //Then a static field like this 

    private static List<Complexity> m_cxList = null; 

    // and method that returns the status lists to bind to DataSource of lists 
    public static List<ComplexityHelper> GetComplexities() 
    { 
     if (m_cxList == null) 
     { 
      string[] list = TranslationHelper.GetTranslation("item_Complexities").Split(','); 
      Array listVal = Enum.GetValues(typeof(Complexities)); 
      if (list.Length != listVal.Length) 
       throw new Exception("Invalid Complexities translations (item_Complexities)"); 
      m_cxList = new List<Complexity>(); 
      for (int i = 0; i < list.Length; i++) 
      { 
      Complexity cx = (ComplexitylistVal.GetValue(i); 
      ComplexityHelper ch = new ComplexityHelper(cx, list[i]); 
      m_cxList.Add(ch); 
      } 
     } 
     return m_cxList; 
    } 
} 

Trong khi hoàn toàn khả thi , Tôi không hài lòng với nó, vì tôi phải viết mã tương tự cho nhiều enums khác nhau mà tôi cần sử dụng trong danh sách chọn.

Có ai có đề xuất về giải pháp đơn giản hoặc chung chung hơn không?

Cảm ơn Bogdan

+0

Xem

Trả lời

1

Cảm ơn tất cả các bạn cho tất cả các câu trả lời. Cuối cùng tôi đã sử dụng kết hợp từ Rex M và adrianbanks, và thêm các cải tiến của riêng tôi, để đơn giản hóa việc liên kết với ComboBox.

Các thay đổi là cần thiết vì, trong khi làm việc trên mã, tôi nhận ra đôi khi tôi cần để có thể loại trừ một mục liệt kê khỏi kết hợp. Ví dụ:

Enum Complexity 
{ 
    // this will be used in filters, 
    // but not in module where I have to assign Complexity to a field 
    AllComplexities, 
    NotSoComplex, 
    LittleComplex, 
    Complex, 
    VeryComplex 
} 

Vì vậy, đôi khi tôi muốn picklist để hiển thị tất cả nhưng AllComplexities (trong add - chỉnh sửa module) và thời gian khác để hiển thị tất cả (trong các bộ lọc)

Dưới đây là những gì tôi đã làm:

  1. Tôi đã tạo phương thức tiện ích mở rộng, sử dụng Thuộc tính mô tả làm khóa tìm kiếm bản địa hóa. Nếu thuộc tính Mô tả bị thiếu, tôi tạo khoá địa phương tra cứu là EnumName_ EnumValue. Cuối cùng, nếu bản dịch bị thiếu, tôi chỉ tách tên enum dựa trên camelcase để tách các từ như được hiển thị bởi adrianbanks. BTW, TranslationHelper là một trình bao bọc xung quanh resourceMgr.GetString (...)

Mã đầy đủ được hiển thị bên dưới

public static string GetDescription(this System.Enum value) 
{ 
    string enumID = string.Empty; 
    string enumDesc = string.Empty; 
    try 
    {   
     // try to lookup Description attribute 
     FieldInfo field = value.GetType().GetField(value.ToString()); 
     object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true); 
     if (attribs.Length > 0) 
     { 
      enumID = ((DescriptionAttribute)attribs[0]).Description; 
      enumDesc = TranslationHelper.GetTranslation(enumID); 
     } 
     if (string.IsNullOrEmpty(enumID) || TranslationHelper.IsTranslationMissing(enumDesc)) 
     { 
      // try to lookup translation from EnumName_EnumValue 
      string[] enumName = value.GetType().ToString().Split('.'); 
      enumID = string.Format("{0}_{1}", enumName[enumName.Length - 1], value.ToString()); 
      enumDesc = TranslationHelper.GetTranslation(enumID); 
      if (TranslationHelper.IsTranslationMissing(enumDesc)) 
       enumDesc = string.Empty; 
     } 

     // try to format CamelCase to proper names 
     if (string.IsNullOrEmpty(enumDesc)) 
     { 
      Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled); 
      enumDesc = capitalLetterMatch.Replace(value.ToString(), " $&"); 
     } 
    } 
    catch (Exception) 
    { 
     // if any error, fallback to string value 
     enumDesc = value.ToString(); 
    } 

    return enumDesc; 
} 

Tôi tạo ra một lớp helper generic dựa trên Enum, cho phép để ràng buộc các enum dễ dàng để DataSource

public class LocalizableEnum 
{ 
    /// <summary> 
    /// Column names exposed by LocalizableEnum 
    /// </summary> 
    public class ColumnNames 
    { 
     public const string ID = "EnumValue"; 
     public const string EntityValue = "EnumDescription"; 
    } 
} 

public class LocalizableEnum<T> 
{ 

    private T m_ItemVal; 
    private string m_ItemDesc; 

    public LocalizableEnum(T id) 
    { 
     System.Enum idEnum = id as System.Enum; 
     if (idEnum == null) 
      throw new ArgumentException(string.Format("Type {0} is not enum", id.ToString())); 
     else 
     { 
      m_ItemVal = id; 
      m_ItemDesc = idEnum.GetDescription(); 
     } 
    } 

    public override string ToString() 
    { 
     return m_ItemDesc; 
    } 

    public T EnumValue 
    { 
     get { return m_ID; } 
    } 

    public string EnumDescription 
    { 
     get { return ToString(); } 
    } 

} 

Sau đó, tôi đã tạo ra một phương pháp tĩnh chung trả về Danh sách>, như bên dưới

public static List<LocalizableEnum<T>> GetEnumList<T>(object excludeMember) 
{ 
    List<LocalizableEnum<T>> list =null; 
    Array listVal = System.Enum.GetValues(typeof(T)); 
    if (listVal.Length>0) 
    { 
     string excludedValStr = string.Empty; 
     if (excludeMember != null) 
      excludedValStr = ((T)excludeMember).ToString(); 

     list = new List<LocalizableEnum<T>>(); 
     for (int i = 0; i < listVal.Length; i++) 
     { 
      T currentVal = (T)listVal.GetValue(i); 
      if (excludedValStr != currentVal.ToString()) 
      { 
       System.Enum enumVal = currentVal as System.Enum; 
       LocalizableEnum<T> enumMember = new LocalizableEnum<T>(currentVal); 
       list.Add(enumMember); 
      } 
     } 
    } 
    return list; 
} 

và một wrapper để trở lại danh sách với tất cả các thành viên

public static List<LocalizableEnum<T>> GetEnumList<T>() 
{ 
     return GetEnumList<T>(null); 
} 

Bây giờ chúng ta hãy đặt tất cả mọi thứ lại với nhau và liên kết với kết hợp thực tế:

// in module where we want to show items with all complexities 
// or just filter on one complexity 

comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue; 
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription; 
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>(); 
comboComplexity.SelectedValue = Complexity.AllComplexities; 

// .... 
// and here in edit module where we don't want to see "All Complexities" 
comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue; 
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription; 
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>(Complexity.AllComplexities); 
comboComplexity.SelectedValue = Complexity.VeryComplex; // set default value 

Để đọc chọn giá trị và sử dụng nó, tôi sử dụng mã như bên dưới

Complexity selComplexity = (Complexity)comboComplexity.SelectedValue; 
60

tên thân Basic

Sử dụng Description attribute: *

enum MyEnum 
{ 
    [Description("This is black")] 
    Black, 
    [Description("This is white")] 
    White 
} 

Và một phương pháp khuyến nông có ích cho enums:

public static string GetDescription(this Enum value) 
{ 
    FieldInfo field = value.GetType().GetField(value.ToString()); 
    object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true); 
    if(attribs.Length > 0) 
    { 
     return ((DescriptionAttribute)attribs[0]).Description; 
    } 
    return string.Empty; 
} 

sử dụng như sau:

MyEnum val = MyEnum.Black; 
Console.WriteLine(val.GetDescription()); //writes "This is black" 

(Lưu ý điều này không chính xác làm việc cho cờ chút ...)

Đối nội địa hóa

Có một mô hình tốt được thành lập trong .NET cho xử lý nhiều ngôn ngữ cho mỗi chuỗi giá trị - sử dụng số resource file và mở rộng phương thức mở rộng để đọc từ tệp tài nguyên:

public static string GetDescription(this Enum value) 
{ 
    FieldInfo field = value.GetType().GetField(value.ToString()); 
    object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true)); 
    if(attribs.Length > 0) 
    { 
     string message = ((DescriptionAttribute)attribs[0]).Description; 
     return resourceMgr.GetString(message, CultureInfo.CurrentCulture); 
    } 
    return string.Empty; 
} 

Bất cứ lúc nào chúng ta có thể tận dụng chức năng BCL hiện có để đạt được những gì chúng ta muốn, đó chắc chắn là con đường đầu tiên để khám phá. Điều này giảm thiểu sự phức tạp và sử dụng các mẫu đã quen thuộc với nhiều nhà phát triển khác.

Đưa nó tất cả cùng nhau

Để có được điều này để ràng buộc vào một DropDownList, chúng ta có lẽ muốn theo dõi các giá trị enum thực trong kiểm soát của chúng tôi và hạn chế dịch, tên thân thiện với đường thị giác.Chúng ta có thể làm như vậy bằng cách sử dụng một loại vô danh và các thuộc tính DataField trong danh sách:

<asp:DropDownList ID="myDDL" 
        DataTextField="Description" 
        DataValueField="Value" /> 

myDDL.DataSource = Enum.GetValues(typeof(MyEnum)).OfType<MyEnum>().Select(
    val => new { Description = val.GetDescription(), Value = val.ToString() }); 

myDDL.DataBind(); 

Hãy phá vỡ dòng DataSource:

  • Đầu tiên chúng ta gọi là Enum.GetValues(typeof(MyEnum)), mà được chúng ta một cách lỏng lẻo, đánh máy Array các giá trị
  • Tiếp theo chúng ta gọi OfType<MyEnum>() mà chuyển đổi mảng đến một IEnumerable<MyEnum>
  • sau đó, chúng ta gọi là Select() và cung cấp một lambda rằng dự án mới đối tượng với hai trường, Mô tả và Giá trị.

Thuộc tính DataTextField và DataValueField được đánh giá một cách phản ánh tại thời điểm databind, do đó, chúng sẽ tìm kiếm các trường trên DataItem với các tên phù hợp.

- Lưu ý trong bài viết chính, tác giả đã viết riêng lớp DescriptionAttribute không cần thiết, như đã tồn tại trong thư viện chuẩn của .NET. -

+5

Đối nội địa hóa, bạn có thể bỏ qua các thuộc tính mô tả và chỉ cần sử dụng "MyEnum.Blac k "và" MyEnum.White "làm tên tài nguyên. – stevemegson

+0

@stevemegson chắc chắn sẽ hoạt động! Tôi là một người thuần túy SoC/SRP mặc dù, và với tôi đôi purposing enum như cả một giá trị có lập trình và một khóa tài nguyên đa ngôn ngữ đặt ra chuông cảnh báo :) –

+0

Hi, Những gì tôi muốn thực hiện là sử dụng một ràng buộc như comboComplexity.DataSource = Enum.GetValues ​​(typeof (Phức tạp)); Nhưng sử dụng này, tôi vẫn nhận được trong kết hợp tên của các giá trị liệt kê, và không giới thiệu họ Ngoài ra, tôi đổi tên GetDescription để ToString(), hy vọng kết hợp đó sẽ sử dụng cho tên enum, nhưng may mắn không Bất kỳ gợi ý? Cảm ơn – bzamfir

1

tôi sử dụng lớp sau

public class EnumUtils 
    { 
    /// <summary> 
    ///  Reads and returns the value of the Description Attribute of an enumeration value. 
    /// </summary> 
    /// <param name="value">The enumeration value whose Description attribute you wish to have returned.</param> 
    /// <returns>The string value portion of the Description attribute.</returns> 
    public static string StringValueOf(Enum value) 
    { 
     FieldInfo fi = value.GetType().GetField(value.ToString()); 
     DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); 
     if (attributes.Length > 0) 
     { 
      return attributes[0].Description; 
     } 
     else 
     { 
      return value.ToString(); 
     } 
    } 

    /// <summary> 
    ///  Returns the Enumeration value that has a given Description attribute. 
    /// </summary> 
    /// <param name="value">The Description attribute value.</param> 
    /// <param name="enumType">The type of enumeration in which to search.</param> 
    /// <returns>The enumeration value that matches the Description value provided.</returns> 
    /// <exception cref="ArgumentException">Thrown when the specified Description value is not found with in the provided Enumeration Type.</exception> 
    public static object EnumValueOf(string value, Type enumType) 
    { 
     string[] names = Enum.GetNames(enumType); 
     foreach (string name in names) 
     { 
      if (StringValueOf((Enum)Enum.Parse(enumType, name)).Equals(value)) 
      { 
       return Enum.Parse(enumType, name); 
      } 
     } 

     throw new ArgumentException("The string is not a description or value of the specified enum."); 
    } 

nào đọc một thuộc tính gọi là mô tả

public enum PuppyType 
{ 
    [Description("Cute Puppy")] 
    CutePuppy = 0, 
    [Description("Silly Puppy")] 
    SillyPuppy 
} 
4

Việc sử dụng các thuộc tính như trong các câu trả lời khác là một cách tốt để đi, nhưng nếu bạn chỉ muốn sử dụng văn bản từ các giá trị của enum, mã sau sẽ được chia dựa trên vỏ lạc đà của giá trị:

public static string GetDescriptionOf(Enum enumType) 
{ 
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled); 
    return capitalLetterMatch.Replace(enumType.ToString(), " $&"); 
} 

Gọi GetDescriptionOf(Complexity.NotSoComplex) sẽ trả lại Not So Complex. Điều này có thể được sử dụng với bất kỳ giá trị enum nào.

Để làm cho nó hữu ích hơn, bạn có thể làm cho nó trở thành một phương pháp khuyến nông:

public static string ToFriendlyString(this Enum enumType) 
{ 
    Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled); 
    return capitalLetterMatch.Replace(enumType.ToString(), " $&"); 
} 

Bạn Cal nay gọi nó là sử dụng Complexity.NotSoComplex.ToFriendlyString() trở Not So Complex.


EDIT: chỉ cần nhận thấy rằng trong câu hỏi của bạn bạn đề cập rằng bạn cần phải dịch văn bản. Trong trường hợp đó, tôi sẽ sử dụng thuộc tính để chứa khóa để tra cứu giá trị được bản địa hoá, nhưng mặc định là phương thức chuỗi thân thiện làm phương sách cuối cùng nếu không tìm thấy văn bản được bản địa hóa.Bạn sẽ xác định bạn enums như thế này:

enum Complexity 
{ 
    [LocalisedEnum("Complexity.NotSoComplex")] 
    NotSoComplex, 
    [LocalisedEnum("Complexity.LittleComplex")] 
    LittleComplex, 
    [LocalisedEnum("Complexity.Complex")] 
    Complex, 
    [LocalisedEnum("Complexity.VeryComplex")] 
    VeryComplex 
} 

Bạn cũng sẽ cần mã này:

[AttributeUsage(AttributeTargets.Field, AllowMultiple=false, Inherited=true)] 
public class LocalisedEnum : Attribute 
{ 
    public string LocalisationKey{get;set;} 

    public LocalisedEnum(string localisationKey) 
    { 
     LocalisationKey = localisationKey; 
    } 
} 

public static class LocalisedEnumExtensions 
{ 
    public static string ToLocalisedString(this Enum enumType) 
    { 
     // default value is the ToString(); 
     string description = enumType.ToString(); 

     try 
     { 
      bool done = false; 

      MemberInfo[] memberInfo = enumType.GetType().GetMember(enumType.ToString()); 

      if (memberInfo != null && memberInfo.Length > 0) 
      { 
       object[] attributes = memberInfo[0].GetCustomAttributes(typeof(LocalisedEnum), false); 

       if (attributes != null && attributes.Length > 0) 
       { 
        LocalisedEnum descriptionAttribute = attributes[0] as LocalisedEnum; 

        if (description != null && descriptionAttribute != null) 
        { 
         string desc = TranslationHelper.GetTranslation(descriptionAttribute.LocalisationKey); 

         if (desc != null) 
         { 
          description = desc; 
          done = true; 
         } 
        } 
       } 
      } 

      if (!done) 
      { 
       Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled); 
       description = capitalLetterMatch.Replace(enumType.ToString(), " $&"); 
      } 
     } 
     catch 
     { 
      description = enumType.ToString(); 
     } 

     return description; 
    } 
} 

Để có được giới thiệu địa phương, sau đó bạn sẽ gọi:

Complexity.NotSoComplex.ToLocalisedString() 

này có một số trường hợp dự phòng:

  • nếu ENU m có một thuộc tính LocalisedEnum xác định, nó sẽ sử dụng chìa khóa để tìm kiếm các văn bản dịch
  • nếu enum có một thuộc tính LocalisedEnum định nghĩa nhưng không có văn bản cục bộ được tìm thấy, nó mặc định để sử dụng lạc đà hợp cụ thể phân chia phương pháp
  • nếu enum không có một thuộc tính LocalisedEnum xác định, nó sẽ sử dụng lạc đà hợp cụ thể phân chia phương pháp
  • khi bất kỳ lỗi, nó mặc định là ToString giá trị enum
+0

Xin chào Cảm ơn bạn đã trả lời. Tôi đã thử đề xuất của bạn và cố gắng kết hợp combo để enum với mã như thế này comboComplexity.DataSource = Enum.GetValues ​​(typeof (Phức tạp)); Nhưng điều này làm cho danh sách chỉ hiển thị tên enum mặc định Tôi đổi tên thành ToLocalizedString() thành ToString() (biết rằng điều này thực sự được gọi là Combo) nhưng vẫn không hoạt động. Bất kỳ đề xuất nào? Tôi muốn một ràng buộc đơn giản như thế này, vì với một enum với 20 + giá trị, thêm tất cả các giá trị một sẽ là một nỗi đau Cảm ơn – bzamfir

+0

Bằng cách đổi tên ToLocalizedString() thành ToString(), bạn không ghi đè ToString() phương pháp của enum. Thay vào đó, bạn đã tạo một phương thức mở rộng có cùng tên với phương thức ToString() hiện có của enum.Gọi ToString() sẽ gọi phương thức hiện có chứ không phải phương thức mở rộng. Bạn sẽ phải nhận các chuỗi được bản địa hóa cho mỗi giá trị enum và databind đến tập hợp các chuỗi đó. – adrianbanks

Các vấn đề liên quan