Sunday, June 20, 2010

Bypassing the Property Grid's Sort Order

While working on a programming project one of the things I wanted to include was the ability to list out the values in the collection in the same way the array lists its items.

Normally, the collection displays an ellipsis button, allowing the user to manipulate the collection, but does not allow the user to expand the list of items like an array does. I figured this would be very simple to add. The .NET framework provides a base class for an object called a TypeConverter. The TypeConverter allows objects of various types to be translated to and from various representations.


Beyond that, the TypeConverter can supply a list of standard values (Enumerators use this, which allows the property grid to display a drop-down list containing the various valid values.) The TypeConverter can also supply an editor class for the type (You see this in form designers all the time, the font dialog, the drop-down for colors, the drop-down for for docking, etc...) Finally, the type converter can also supply a list of properties that the object can display as child properties of a property. It is this that I will focus on:

public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
List<String> lst = (List<String>)value;
ListItemDescriptor[] props = new ListItemDescriptor[lst.Count];

for (int i = 0; i < lst.Count; i++)
props[i] = new ListItemDescriptor(i);

return new PropertyDescriptorCollection(props);
}

The above code demonstrates how the properties are provided by the TypeDescriptor. Using the above code results in the following list in the grid:
Far from optimal for dealing with a list of items. The list is sorted alphabetically. But arrays seem to sort by index. That was what I wanted, so I began spelunking through the reference source code of the .NET framework. Examining the Array code and ArrayConverter code revealed nothing. They were doing essentially what I was doing. I then explored the grid. It appears the PropertyGrid itself considers an array a special case and sorts the items by index. That is no use to me. 
Using the sort method of the PropertyDescriptorCollection object does nothing, since the grid will always override it with the user's sort preference. So, how to sort the items by index?
The secret is in the property descriptor collection. Replacing the return statement above with the following:


return new ListPropertyDescriptorCollection(props);

The following listing shows the ListPropertyDescriptorCollection class that does the magic:
private class ListPropertyDescriptorCollection : PropertyDescriptorCollection
{ 
    public ListPropertyDescriptorCollection(PropertyDescriptor[] props)
    : base(props)
   {
  
    }

    public override PropertyDescriptorCollection Sort
    (System.Collections.IComparer comparer) 
   { 
        if (comparer.GetType().Name == "DisplayNameSortComparer") 
       { 
            ListItemDescriptor.ListItemDescriptorSortComparer comp = 
             new ListItemDescriptor.ListItemDescriptorSortComparer(); 
            return Sort(comp); 
       } 
         else 
       { 
            return base.Sort(comparer);
       }
    }
 }


The code that performs the trick should be rather self-evident:

ListItemDescriptor.ListItemDescriptorSortComparer comp = new ListItemDescriptor.ListItemDescriptorSortComparer();
 return Sort(comp);

Two lines of code in this function do all that is necessary to force the numeric sort. The comparer in this case compares the indicies of the descriptors, causing the sort function to sort by the numeric index. This completely replaces any sort function used with the desired sort, thereby forcing a sort by index. 


 

No comments: