.Net Forms PropertyGrid ignores DisplayNameAttribute when sorting properties for multiple selected objects

Is there a way to make the .NET Forms PropertyGrid equal to DisplayNameAttribute when it sorts the properties of several selected objects. When one object selects PropertyGrid sorts based on DisplayNameAttribute , but when multiple objects are selected, it uses the actual property name to sort.

The following code demonstrates the problem:

 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; } } 

The previous code does Form with two PropertyGrids . The left grid contains the only object of its choice, while the right grid contains two objects of its choice.

enter image description here

All objects are of the same type. The left grid sorts properties based on DisplayNameAttribute , while the correct sorts are based on the actual name of the property. In both cases, the DisplayNameAttribute appears as the name of the property in the grid:

Can I force a PropertyGrid always use DisplayNameAttribute when sorting ?

+7
c # winforms
source share
2 answers

So, I found the answer to my problem. Yes, perhaps " force ", or, perhaps more correctly, " trick ", PropertyGrid , always observing DisplayName when sorting. The essence of the problem was that the PropertyGrid uses the actual name of the property when sorting the properties of several selected objects. Therefore, to get the desired behavior, we must make the grid assume that DisplayName is the actual name of the property. PropertyGrid uses PropertyDescriptors to detect various property attributes of objects. We just need a custom PropertyDescriptor that will represent the DisplayName as the actual property name. Here is the following code:

 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); } } 

The previous class is used to migrate an existing PropertyDescriptor and override the behavior of the Name property. The Name property will now return DisplayName (if not empty or empty) or the name of the actual property. All other functions are delegated to the wrapped PropertyDescriptor .

So, now we have a way to change the name of the presented property, we just need the PropertyGrid use the new PropertyDescriptor . To do this, we need to configure TypeDescriptor . Let's look at the following code again:

 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()); } } 

This class inherits from ExpandableObjectConverter to use its existing behavior and minimize our implementation. We only need to override the GetProperties method. These methods query the base type to get the corresponding PropertyDescriptorCollection , and then wrap all the elements of this collection in our DisplayNameEnforcingDescriptor . The new collection back for our wrapped items.

Now, if we assign the MyObject class to DisplayNameEnforcingConverter , sorting will always be done based on the DisplayName properties

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

enter image description here

+2
source share

The docs for PropertyGrid.PropertySortOrder say:

To perform custom sorting, add ICustomTypeDescriptor to the component to return the properties in the desired order.

I modified your class to implement ICustomTypeDescriptor , and while ICustomTypeDescriptor methods have ICustomTypeDescriptor called and returned properties in order by the DisplayName attribute, PropertyGrid.SelectObjects , I still insist on sorting by the actual property name:

 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; } } 

Since this does not work, it seems that the answer to your question is: No, you cannot force the PropertyGrid sort by DisplayNameAttribute when selecting multiple objects.

0
source share

All Articles