Данные связывают свойства перечисления с сеткой и описанием отображения

Это аналогичный вопрос Как связать пользовательское описание Enum с DataGrid, но в моем случае у меня есть несколько свойств.

public enum ExpectationResult
{
    [Description("-")]
    NoExpectation,

    [Description("Passed")]
    Pass,

    [Description("FAILED")]
    Fail
}

public class TestResult
{
    public string TestDescription { get; set; }
    public ExpectationResult RequiredExpectationResult { get; set; }
    public ExpectationResult NonRequiredExpectationResult { get; set; }
}

Я привязываю BindingList <TestResult> к WinForms DataGridView (на самом деле DevExpress.XtraGrid.GridControl, но универсальное решение будет более широко применяться). Я хочу, чтобы описания отображались, а не имена перечислений. Как я могу это сделать? (Нет ограничений на атрибуты class/enum/, я могу их по желанию изменять.)

Ответ 1

A TypeConverter обычно выполняет эту работу; здесь некоторый код, который работает для DataGridView - просто добавьте в свой код, чтобы прочитать описания (через отражение и т.д. - я только что использовал строковый префикс, чтобы показать, что пользовательский код работает).

Обратите внимание, что вы, вероятно, захотите также переопределить ConvertFrom. Конвертер может быть указан на уровне или уровне свойства (в случае, если вы хотите, чтобы он был применен для некоторых свойств), а также может применяться во время выполнения, если перечисление не находится под вашим контролем.

using System.ComponentModel;
using System.Windows.Forms;
[TypeConverter(typeof(ExpectationResultConverter))]
public enum ExpectationResult
{
    [Description("-")]
    NoExpectation,

    [Description("Passed")]
    Pass,

    [Description("FAILED")]
    Fail
}

class ExpectationResultConverter : EnumConverter
{
    public ExpectationResultConverter()
        : base(
            typeof(ExpectationResult))
    { }

    public override object ConvertTo(ITypeDescriptorContext context,
        System.Globalization.CultureInfo culture, object value,
        System.Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            return "abc " + value.ToString(); // your code here
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

public class TestResult
{
    public string TestDescription { get; set; }
    public ExpectationResult RequiredExpectationResult { get; set; }
    public ExpectationResult NonRequiredExpectationResult { get; set; }

    static void Main()
    {
        BindingList<TestResult> list = new BindingList<TestResult>();
        DataGridView grid = new DataGridView();
        grid.DataSource = list;
        Form form = new Form();
        grid.Dock = DockStyle.Fill;
        form.Controls.Add(grid);
        Application.Run(form);
    }
}

Ответ 2

Я не уверен, насколько это помогает, но я использую метод расширения для Enum, который выглядит следующим образом:

    /// <summary>
    /// Returns the value of the description attribute attached to an enum value.
    /// </summary>
    /// <param name="en"></param>
    /// <returns>The text from the System.ComponentModel.DescriptionAttribute associated with the enumeration value.</returns>
    /// <remarks>
    /// To use this, create an enum and mark its members with a [Description("My Descr")] attribute.
    /// Then when you call this extension method, you will receive "My Descr".
    /// </remarks>
    /// <example><code>
    /// enum MyEnum {
    ///     [Description("Some Descriptive Text")]
    ///     EnumVal1,
    ///
    ///     [Description("Some More Descriptive Text")]
    ///     EnumVal2
    /// }
    /// 
    /// static void Main(string[] args) {
    ///     Console.PrintLine( MyEnum.EnumVal1.GetDescription() );
    /// }
    /// </code>
    /// 
    /// This will result in the output "Some Descriptive Text".
    /// </example>
    public static string GetDescription(this Enum en)
    {
        var type = en.GetType();
        var memInfo = type.GetMember(en.ToString());

        if (memInfo != null && memInfo.Length > 0)
        {
            var attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (attrs != null && attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }
        return en.ToString();
    }

Вы можете использовать пользовательский атрибут свойства на своем объекте, чтобы вернуть имя:

public class TestResult
{
    public string TestDescription { get; set; }
    public ExpectationResult RequiredExpectationResult { get; set; }
    public ExpectationResult NonRequiredExpectationResult { get; set; }

    /* *** added these new property getters *** */
    public string RequiredExpectationResultDescr { get { return this.RequiredExpectationResult.GetDescription(); } }
    public string NonRequiredExpectationResultDescr { get { return this.NonRequiredExpectationResult.GetDescription(); } }
}

Затем привяжите сетку к свойствам RequiredExpectationResultDescr и NonRequiredExpectationResultDescr.

Это может быть немного сложнее, но его первое, что я придумал:)

Ответ 3

Основываясь на двух других ответах, я собрал класс, который может в общем случае конвертировать между произвольным перечислением и строкой с использованием атрибута Description для каждого значения перечисления.

Это использует System.ComponentModel для определения DescriptionAttribute и поддерживает только преобразование между T и String.

public class EnumDescriptionConverter<T> : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return (sourceType == typeof(T) || sourceType == typeof(string));
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType == typeof(T) || destinationType == typeof(string));
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        Type typeFrom = context.Instance.GetType();

        if (typeFrom == typeof(string))
        {
            return (object)GetValue((string)context.Instance);
        }
        else if (typeFrom is T)
        {
            return (object)GetDescription((T)context.Instance);
        }
        else
        {
            throw new ArgumentException("Type converting from not supported: " + typeFrom.FullName);
        }
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        Type typeFrom = value.GetType();

        if (typeFrom == typeof(string) && destinationType == typeof(T))
        {
            return (object)GetValue((string)value);
        }
        else if (typeFrom == typeof(T) && destinationType == typeof(string))
        {
            return (object)GetDescription((T)value);
        }
        else
        {
            throw new ArgumentException("Type converting from not supported: " + typeFrom.FullName);
        }
    }

    public string GetDescription(T en)
    {
        var type = en.GetType();
        var memInfo = type.GetMember(en.ToString());

        if (memInfo != null && memInfo.Length > 0)
        {
            var attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (attrs != null && attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }
        return en.ToString();
    }

    public T GetValue(string description)
    {
        foreach (T val in Enum.GetValues(typeof(T)))
        {
            string currDescription = GetDescription(val);
            if (currDescription == description)
            {
                return val;
            }
        }

        throw new ArgumentOutOfRangeException("description", "Argument description must match a Description attribute on an enum value of " + typeof(T).FullName);
    }
}