有什么方法可以在.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;
}
}
Run Code Online (Sandbox Code Playgroud)
之前的代码Form有两个 PropertyGrids.左侧网格在其选择中包含单个对象,而右侧网格在其选择中包含两个对象.

所有对象都属于同一类型.左格排序的properties基础上,DisplayNameAttribute同时根据实际的属性名称正确排序.在这两种情况下,它DisplayNameAttribute都显示为网格中的属性名称:
我可以强制将PropertyGrid总是使用DisplayNameAttribute时排序?
所以我找到了我的问题的答案。是的,可以“ 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);
}
}
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)
