.Net Forms PropertyGrid在对多个选定对象排序属性时忽略DisplayNameAttribute

Car*_*los 7 .net c# winforms

有什么方法可以在.NET Forms PropertyGridDisplayNameAttribute多个选定对象的属性进行排序时获得尊重.当单个对象被选择时,PropertyGrid基于DisplayNameAttribute但是当选择多个对象时,它使用实际的属性名进行排序.

以下代码演示了此问题:

static class Program
{
    [STAThread]
    static void Main()
    {
        Form myForm1 = new Form();
        myForm1.Width = 820;
        myForm1.Height = 340;

        PropertyGrid grid1 = new PropertyGrid();
        grid1.Left = 0;
        grid1.Top = 0;
        grid1.Width = 400;
        grid1.Height = 300;
        myForm1.Controls.Add(grid1);

        grid1.SelectedObject = new MyObject();

        PropertyGrid grid2 = new PropertyGrid();
        grid2.Left = 400;
        grid2.Top = 0;
        grid2.Width = 400;
        grid2.Height = 300;
        myForm1.Controls.Add(grid2);

        object[] objects = new object[] { new MyObject(), new MyObject() };
        grid2.SelectedObjects = objects;

        Application.Run(myForm1);
    }
}


public class MyObject
{
    [DisplayName("ZZZZ")]
    public int AProperty
    {
        get;
        set;
    }

    [DisplayName("BBBB")]
    public int BProperty
    {
        get;
        set;
    }

}
Run Code Online (Sandbox Code Playgroud)

之前的代码Form两个 PropertyGrids.左侧网格在其选择中包含单个对象,而右侧网格在其选择中包含两个对象.

在此输入图像描述

所有对象都属于同一类型.左格排序properties基础上,DisplayNameAttribute同时根据实际的属性名称正确排序.在这两种情况下,它DisplayNameAttribute都显示为网格中的属性名称:

我可以强制PropertyGrid总是使用DisplayNameAttribute排序

Car*_*los 2

所以我找到了我的问题的答案。是的,可以“ force”,或者更正确地“ trick”,排序时PropertyGrid始终尊重“” DisplayName。问题的核心在于,“PropertyGrid”在对多个选定对象的属性进行排序时使用实际的属性名称。因此,为了获得所需的行为,我们必须让网格相信这DisplayName是实际的属性名称。用于发现对象属性的各种属性PropertyGridPropertyDescriptors我们只需要一个PropertyDescriptor将 呈现DisplayName为实际属性名称的自定义。看下面的代码:

public class DisplayNameEnforcingDescriptor : PropertyDescriptor
{
    private PropertyDescriptor _descriptor;
    public DisplayNameEnforcingDescriptor(PropertyDescriptor descriptor)
        : base(descriptor)
    {
        this._descriptor = descriptor;
    }

    public override string Name
    {
        get
        {
            return string.IsNullOrEmpty(DisplayName) ? base.Name : DisplayName;
        }
    }

    public override bool CanResetValue(object component)
    {
        return _descriptor.CanResetValue(component);
    }

    public override Type ComponentType
    {
        get
        {
            return _descriptor.ComponentType;
        }
    }

    public override object GetValue(object component)
    {
        return _descriptor.GetValue(component);
    }

    public override bool IsReadOnly
    {
        get
        {
            return _descriptor.IsReadOnly;
        }
    }

    public override Type PropertyType
    {
        get
        {
            return _descriptor.PropertyType;
        }
    }

    public override void ResetValue(object component)
    {
        _descriptor.ResetValue(component);
    }

    public override void SetValue(object component, object value)
    {
        _descriptor.SetValue(component, value);
    }

    public override bool ShouldSerializeValue(object component)
    {
        return _descriptor.ShouldSerializeValue(component);
    }
}
Run Code Online (Sandbox Code Playgroud)

前一个类用于包装现有的PropertyDescriptor并覆盖“Name”属性的行为。该Name属性现在将返回DisplayName(如果不为 null 或空)或实际属性名称。所有其他功能都委托给包装的PropertyDescriptor.

所以现在我们有办法改变所呈现的属性名称,我们只需要使用PropertyGrid新的PropertyDescriptor. 为此,我们需要一个定制的TypeDescriptor. 再次看下面的代码:

public class DisplayNameEnforcingConverter : ExpandableObjectConverter
{
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        PropertyDescriptorCollection original = base.GetProperties(context, value, attributes);
        List<DisplayNameEnforcingDescriptor> descriptorList = new List<DisplayNameEnforcingDescriptor>();
        foreach (PropertyDescriptor descriptor in original)
            descriptorList.Add(new DisplayNameEnforcingDescriptor(descriptor));
        return new PropertyDescriptorCollection(descriptorList.ToArray());
    }
}
Run Code Online (Sandbox Code Playgroud)

该类继承自ExpandableObjectConverter以利用其现有行为并最小化我们的实现。我们只需要重写该GetProperties方法。此方法要求基类型获取相关信息PropertyDescriptorCollection,然后将该集合的所有元素包装在我们的DisplayNameEnforcingDescriptor. collection返回一个新的包含我们包装的元素。

现在,如果我们MyObject为类添加属性DisplayNameEnforcingConverter,排序将始终基于属性进行DisplayName

[TypeConverter(typeof(DisplayNameEnforcingConverter))]
public class MyObject
{
    [DisplayName("ZZZZ")]
    public int AProperty
    {
        get;
        set;
    }

    [DisplayName("BBBB")]
    public int BProperty
    {
        get;
        set;
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述