7

有没有办法在对多个选定对象的属性进行排序时做出.NET Forms PropertyGrid尊重。DisplayNameAttribute当单个对象被选择时,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;
    }

}

前面的代码使 aForm两个 PropertyGrids。左侧网格在其选择中包含单个对象,而右侧网格在其选择中包含两个对象。

在此处输入图像描述

所有对象都属于同一类型。左侧网格根据实际属性名称进行排序,而右侧网格properties根据DisplayNameAttribute实际属性名称进行排序。在这两种情况下,DisplayNameAttribute都在网格中显示为属性名称:

我可以强制PropertyGrid总是在排序DisplayNameAttribute时使用吗?

4

2 回答 2

2

所以我找到了我的问题的答案。是的,可以“ force”,或者更准确地说“ trick”,PropertyGrid总是尊重DisplayName排序时的。问题的核心在于“PropertyGrid”在对多个选定对象的属性进行排序时使用实际属性名称。因此,为了获得所需的行为,我们必须让网格相信DisplayName是实际的属性名称。PropertyGrid用于发现对象属性的PropertyDescriptors各种属性。我们只需要一个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);
    }
}

上一个类用于包装现有的PropertyDescriptor并覆盖“名称”属性的行为。该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());
    }
}

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

现在,如果我们MyObjectDisplayNameEnforcingConverterDisplayName

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

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

在此处输入图像描述

于 2013-07-12T22:33:51.147 回答
0

PropertyGrid.PropertySortOrder的文档指出:

要进行自定义排序,请在组件上实现ICustomTypeDescriptor以按所需顺序返回属性。

我修改了你的类来实现ICustomTypeDescriptor,当ICustomTypeDescriptor方法被调用并按属性按顺序返回属性时DisplayNamePropertyGrid.SelectObjects仍然坚持按实际属性名称排序:

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

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

    public PropertyDescriptorCollection GetProperties()
    {
        // Creates a new collection and assign it the properties for button1.
        var properties = TypeDescriptor.GetProperties(this)
                          .OfType<PropertyDescriptor>();
        var result = properties.OrderBy(x => x.DisplayName);
        return new PropertyDescriptorCollection(result.ToArray());
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var properties = TypeDescriptor.GetProperties(this, attributes, true)
                          .OfType<PropertyDescriptor>();
        var result = properties.OrderBy(x => x.DisplayName);
        return new PropertyDescriptorCollection(result.ToArray());
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this, true);
    }

    public string GetClassName()
    {
        return TypeDescriptor.GetClassName(this, true);
    }

    public string GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }

    public EventDescriptor GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        return TypeDescriptor.GetDefaultProperty(this, true);
    }

    public object GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }
}

由于这不起作用,您的问题的答案似乎是,不,您不能在选择多个对象时强制PropertyGrid排序。DisplayNameAttribute

于 2013-07-11T03:06:59.513 回答