DataGrid with Customizable Column Sorting






4.58/5 (11 votes)
Reusable extension of the DataGrid control that allows the customization of the sorting event for each column of the grid.

Contents
Introduction
While working on a project, I was required to change the default behavior of a DataGrid
: when clicking one precise column, I needed to sort the contents of the grid not by the column the user was clicking, but by another two columns in the grid. Searching the web, I found that several people has a need more or less similar to mine (sorting the grid by all columns except one, sorting the grid remembering the last column the grid was sorted by and combining the sorting with the new column selected, etc.).
So I decided to extend the DataGrid
so that the developer could define specific sorting events that will be raised when the column they have chosen has been clicked. This way we can ignore one column and not sort it or make a complex sorting using several columns or... whatever we need (I hope :D).
Background
The basics of working with DataGrid
, I suppose.
Using the Code
Using the control is quite easy, first add the CSSDataGrid
(which is, in fact, the control that extends the DataGrid
) to the form that is being developed and then follow these two steps:
- Call the
AddNewCustomizedSortingEvent
to add a new specific sort event for the table and the column we like to theCSSDataGrid
in the form that's being created:this.ccsDataGrid1.AddNewCustomizedSortingEvent("ParentTable","ParentItem", new SortDataGridColumnEventHandler(this.OrderParentItemColumn));
- Implement the sorting event. Here two examples are presented, the first one showing how to ignore the sorting for the column (quite easy), and the second one showing how to sort the grid using two columns, instead of the one clicked:
private void OrderParentItemColumn(object sender, SortDataGridColumnEventArgs e) { }
private void OrderChildItemColumn(object sender, SortDataGridColumnEventArgs e) { if (e.DataViewToSort.Sort == "ParentItem, childID DESC") e.DataViewToSort.Sort = "ParentItem DESC, childID DESC"; else e.DataViewToSort.Sort = "ParentItem, childID DESC"; }
As you may see, this way we have access to the view that is currently shown in the grid, so we can also filter the view or many other things that are not in the scope of the article.
We may also clear programmatically the sort events of the CSSDataGrid
:
this.ccsDataGrid1.ClearAllCustomizedSortEvents();
How It Works?
Classes to Manage Sort Events
First of all, SortDataGridColumnEventArgs
are created to be passed to a sort event declared as a delegate, SortDataGridColumnEventHandler
. The SortDataGridColumnEventArgs
has as fields the name and index of the column that has been clicked and the current DataView
that is being shown in the DataGrid
.
public class SortDataGridColumnEventArgs : EventArgs
{
#region Fields
private int _columnIndex;
private string _columnName;
private DataView _viewToSort;
#endregion
...
}
The rest of the class contains a constructor to assign the fields and properties to access them.
Extension of the DataGrid
Now it's time to extend the DataGrid
and implement the CCSDataGrid
. I will store the SortDataGridColumnEventHandler
indexed by the column in Hashtable
that is stored in another Hashtable
indexed by the table name. The root Hashtable
is the only field needed in the CCSDataGrid
control.
/// Hashtable to store hashtables, one per each table in the dataset, and each one of
/// them containing column names and its respective customized sorting events
private Hashtable _hashColumnSortingEvents;
We have a couple of properties to access the CurrentView
in the DataGrid
, and the CurrentTableSortColumnEvents
. Also there are several methods to add events to columns and to clear them:
#region Properties
/// Return the CurrentTable Hashtable with the sort column events
protected Hashtable CurrentTableSortColumnEvents
{
get
{
if (this.CurrentView == null)
return null;
else if (_hashColumnSortingEvents[this.CurrentView.Table.TableName] == null)
return null;
else
return (Hashtable)
_hashColumnSortingEvents[this.CurrentView.Table.TableName];
}
}
/// Gets the currently visible DataView
/// Returns null when no DataView is set.
public DataView CurrentView
{
get
{
if (base.ListManager == null)
return null;
else
return base.ListManager.List as DataView;
}
}
#endregion
/******************
* PUBLIC METHODS *
******************/
#region Public method to add the sorting delegate method to the column
/// Add a Customized Sort Event to the column passed in the table passed too
public bool AddNewCustomizedSortingEvent(string tableName, int column,
SortDataGridColumnEventHandler theEvent)
{
try
{
if (this.CurrentView != null)
{
if ((column < this.CurrentView.Table.Columns.Count) &&
(column > 0))
{
AddNewCustomizedSortingEvent(tableName,
this.CurrentView.Table.Columns[column].ColumnName,
theEvent);
return true;
}
}
return false;
}
catch
{
return false;
}
}
/// Add a Customized Sort Event to the column passed in the table passed too
public void AddNewCustomizedSortingEvent(string tableName, string columnName,
SortDataGridColumnEventHandler theEvent)
{
if (this._hashColumnSortingEvents[tableName] == null)
this._hashColumnSortingEvents[tableName] = new Hashtable();
((Hashtable) this._hashColumnSortingEvents[tableName])[columnName] = theEvent;
}
#endregion
#region Clear the sort events
/// Clear all the customized sort events
public void ClearAllCustomizedSortEvents()
{
this._hashColumnSortingEvents.Clear();
}
/// Clear the customized sort events for the table passed
public void ClearCustomizedSortEventsForTable(string tableName)
{
if (this._hashColumnSortingEvents[tableName] != null)
((Hashtable) this._hashColumnSortingEvents[tableName]).Clear();
}
#endregion
The trick in the control is in the OnMouseDown
event handler, where we'll check if a column header has been clicked and if it had a SortDataGridColumnEventHandler
assigned. If so, that handler is called:
protected override void OnMouseDown(MouseEventArgs e)
{
Point pt = new Point(e.X, e.Y);
DataGrid.HitTestInfo hti = this.HitTest(pt);
// If the selected column is one with a customized sorting event then
// the event is called, otherwise continue with the OnMouseDown event
// Get the sort event if it exists...
SortDataGridColumnEventHandler sortEvent = null;
if ( (hti.Column > 0) && (this.CurrentTableSortColumnEvents != null) )
{
sortEvent = (SortDataGridColumnEventHandler)
this.CurrentTableSortColumnEvents[
this.CurrentView.Table.Columns[hti.Column].ColumnName];
}
// Then if exists the event is called
if( (hti.Type == HitTestType.ColumnHeader) &&
(sortEvent != null) &&
(this.CurrentView != null) )
{
// Call the customized sorting event
SortDataGridColumnEventArgs sortEventArgs =
new SortDataGridColumnEventArgs(hti.Column,
this.CurrentView.Table.Columns[hti.Column].ColumnName,this.CurrentView);
sortEvent(this,sortEventArgs);
}
else
{
base.OnMouseDown(e);
}
}
Points of Interest
In the code that's attached to the article, the CCSDataGrid
does not extend the DataGrid
but an LCGBaseDataGrid
, that is a base class where I'll put all the basic methods, fields and properties that I want to be inherited by all the extensions that I may do. In fact, the property CurrentView
is declared there.
History
- September 2006: First release, my very first article on CodeProject