Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

.net

winforms

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

The following code demonstrates the issue:

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 makes a Form with two PropertyGrids. The left grid contains a single object in its selection, while the right grid contains two objects in its selection.

enter image description here

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

Can I force the PropertyGrid to always use the DisplayNameAttribute when sorting?

like image 627
Carlos Avatar asked Jul 10 '13 23:07

Carlos


1 Answers

So I have found an answer to my problem. Yes it is possible to "force",or perhaps more correctly "trick", the PropertyGrid into always respecting the DisplayName when sorting. The heart of the issue was in the fact the the 'PropertyGrid' uses the actual property name when sorting the properties of multiple selected object. So in order to get the desired behavior, we have to make the grid believe that the DisplayName is the actual property name. The PropertyGrid uses PropertyDescriptors to discover the various attributes of an objects properties. We simply need a custom PropertyDescriptor that will present the DisplayName as the actual property name. Behold 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 wrap an existing PropertyDescriptor and override the behavior of the 'Name' property. The Name property will now return the DisplayName (if not null or empty) or the actual properties name. All other functionality is delegated to the wrapped PropertyDescriptor.

So now we have a way to alter the presented property name, we just need to have the PropertyGrid utilize the new PropertyDescriptor. To do so we need a customized TypeDescriptor. Once again, behold the following code:

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 utilize its existing behavior and minimize our implementation. We only need to override the GetProperties method. This methods asks the base type to get the relevant PropertyDescriptorCollection then wraps all the elements of this collection in our DisplayNameEnforcingDescriptor. A new collection is returned contaning our wrapped elements.

Now if we attribute the MyObject class with the DisplayNameEnforcingConverter, sorting will always happen based on a properties DisplayName

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

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

enter image description here

like image 111
Carlos Avatar answered Oct 26 '22 04:10

Carlos