Sorting DataGrid programmatically






3.27/5 (13 votes)
Aug 6, 2004
2 min read

224205

2
This code sorts a Windows.Forms.DataGrid programmatically, "emulating" a header click.
Introduction
This article shows, how to programmatically sort a System.Windows.Forms.DataGrid
. In other words, how to emulate a "click" on a column header.
Typically, to sort a DataGrid
, we call the DataView.Sort
method of an underlying DataView
. But what if our DataGrid
is bound to a custom datasource? In this case, there's no DataView
under our DataGrid
!
Background
I was writing an application and the task was to "remember" the way DataGrid
s are sorted. So I needed to save my sortings to the Windows registry and restore it each time my application starts.
The problem was: my DataGrid
was bound to a custom collection, no DataView
s or DataTable
s.
Sorting Basics
If your DataGrid
is bound to some IList
collection and you want your DataGrid
to support sorting - your collection must implement IBindingList
interface. This interface contains a method ApplySort()
among others. When a user clicks a column header, DataGrid
calls the datasource's method ApplySort()
.
ApplySort()
method takes two arguments: property
and direction
.
void IBindingList.ApplySort(PropertyDescriptor property,
ListSortDirection direction)
Each column of your DataGrid
represents some property of an underlying object. And you have to pass this property to ApplySort()
method.
Emulate column header click
I've discovered that simply calling ApplySort()
doesn't work. Calling DataGrid
's OnMouseDown()
protected
method with MouseEventArgs
pointed to a column header also doesn't work!
So, I used System.Reflection
to look deep inside the DataGrid
class and see what happens when a user clicks a header. In the list of DataGrid
's private
members, I've found a private
method ColumnHeaderClicked()
. See what I mean? Bingo.
This method is defined as follows:
private void ColumnHeaderClicked(PropertyDescriptor prop)
So, if we want to sort column number 5, we have to determine the underlying PropertyDescriptor
for this column and pass this PropertyDescriptor
to ColumnHeaderClicked
method. This can be done in two ways.
- Calling for a
GridColumnStyle.PropertyDescriptor
property of a grid column. But this property sometimes returnsnull
if our grid is bound to a custom collection throughMappingName
s. - If our
DataGrid
is bound to a custom collectionMyCollection
, we typically create aTableStyle
and someGridColumnStyles
and assign itsMappingName
s.TableStyle
'sMappingName
would be "MyCollection
" andColumnStyle
'sMappingName
would contain the name of some property this column displays. So, we can get thePropertyDescriptor
object by seeking for the property with the name, equal to the column'sMappingName
.
After we have a PropertyDescriptor
object, we simply invoke a private
method using System.Reflection
.
Now the code:
public class MyDataGrid : DataGrid
{
//sort a column by its index
public void SortColumn(int columnIndex)
{
if(this.DataSource!=null &&
((System.Collections.IList)this.DataSource).Count>0)
{
//discover the TYPE of underlying objects
Type sourceType = ((System.Collections.IList)this.DataSource)[0].GetType();
//get the PropertyDescriptor for a sorted column
//assume TableStyles[0] is used for our datagrid... (change it if necessary)
System.ComponentModel.PropertyDescriptor pd =
this.TableStyles[0].GridColumnStyles[columnIndex].PropertyDescriptor;
//if the above line of code didn't work try to get a propertydescriptor
// via MappingName
if(pd == null)
{
System.ComponentModel.PropertyDescriptorCollection pdc =
System.ComponentModel.TypeDescriptor.GetProperties(sourceType);
pd =
pdc.Find( this.TableStyles[0].GridColumnStyles[columnIndex].MappingName,
false);
}
//now invoke ColumnHeaderClicked method using system.reflection tools
System.Reflection.MethodInfo mi =
typeof(System.Windows.Forms.DataGrid).GetMethod("ColumnHeaderClicked",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
mi.Invoke(this, new object[] { pd });
}
}
}
That's it.