This article aims to provide a way to extend the GridView control to enable it to sort your custom entities.
The custom entities I am discussing are ones like “Customer”, “Order” etc that you may have within your system. With n-layer applications becoming more popular the use of custom entities is increasing. Often users will bind an array of these entities to Microsoft’s GridView control and then struggle when it comes to enable sorting. Unfortunately the GridView control will not automatically sort your custom entities out-of-the-box.
If you bind your array of entities to a GridView and select enable sorting, you will get the following error:
"The GridView ‚GridView1‚ fired event Sorting which wasn't handled."
Extending the GridView control
One way to solve this problem is to handle the “sorting” event on every page that we have a GridView, however there are some disadvantages to this:
- Code duplication when you have multiple pages requiring a sortable GridView.
- You will have to write more code, this could lead to more bugs.
- Code behinds become overcrowded with code that could be placed elsewhere.
To extend the GridView (so that you have your own sortable custom GridView) we must first create a new class that inherits everything from the original GridView. I would recommend this in a separate Class Library project (mine is called CustomServerControls).
- Add a new Class Library by right clicking your solution and select Add > New Project... now select “Class Library” give it a name and select OK.
- Add a new class to your new class library (mines called “SortableGridView”).
- Add a reference to System.Web by right clicking on the references folder > Add Reference selecting the .NET tab and selecting “System.Web” from the list.
- Open your newly created class and inherit from the GridView control (you will have to add a using directive to System.Web.UI.WebControls):
using System;
using System.Web.UI.WebControls;
namespace CustomServerControls
{
public class SortableGridView : GridView
{
}
}
- In the controls constructor wire up the GridView’s Sorting event to an internal method (we will come back to this in a minute).
Obtaining the Sort Direction
Within our new GridView we will need to obtain the sort direction so that we can sort firstly by ascending order then if the column is selected again by descending order.
- Create a new private method called GetSortDirection which will take the sort expression (string) as a parameter (the sort expression is simply the name of the property we wish to sort for example forename... more on this later) and returns a SortDirection.
- Set the default sort direction:
private SortDirection GetSortDirection(string expression)
{
SortDirection directionToReturn = SortDirection.Ascending;
return directionToReturn;
}
- Create a new class wide HashTable called _sortTable which will contain our sort directions in (It has to be class wide so that we can persist this in our control state later). We will save our sort expression as the key and its sort direction as the value.
- Create an if statement to determin whether or not we have previously sorted the column, if we have obtain the sort direction from the hashtable and set the direction to return as the opposite, if we have not then add the sort expression and direction to the hashtable and return the default direction. Here is the completed method:
private SortDirection GetSortDirection(string expression)
{
SortDirection directionToReturn = SortDirection.Ascending;
if(_sortTable.Contains(expression))
{
SortDirection lastDirection = (SortDirection)_sortTable[expression];
if(lastDirection == SortDirection.Ascending)
directionToReturn = SortDirection.Descending;
else
directionToReturn = SortDirection.Ascending;
_sortTable[expression] = directionToReturn;
}
else
{
_sortTable.Add(expression, directionToReturn);
}
return directionToReturn;
}
Persisting the Sort Direction
Our custom GridView will need to store the sort direction otherwise our GridView will only be able to sort in one direction. To enable bi-directional sorting we will use control state so that we persist the previous direction. We could equally use ViewState however this could be disabled on the page which hosts the custom GridView and our bi-directional sorting will no longer work!
- Override the SaveControlState method and add the HashTable to it like this:
protected override object SaveControlState()
{
return new object[] { base.SaveControlState(), _sortTable };
}
- Override the LoadControlState method to load the hash table previously saved or create a new one if one was not saved:
protected override void LoadControlState(object savedState)
{
object[] gridViewState = (object[])savedState;
base.LoadControlState(gridViewState[0]);
_sortTable = (Hashtable)gridViewState[1];
if(_sortTable == null)
_sortTable = new Hashtable();
}
Back to the Sorting Event...
Now we have most all of the other methods in place we can continue to code our Sorting method that was started at the beginning of this tutorial.
We will obtain the sort direction from our GetSortDirection() method we created earlier (this will be used in a minute) and then cast the GridView’s DataSource over to an Array (the collections we are binding to the GridView is an Array). Now we have the Array we can sort it using the Arrays static method Sort() passing in our datasource. The problem with this is that it will perform a default sort on the items, and will not sorted it by the expression we have provided.
To sort by the selected sort expression we need to create a new class that implements IComparer. When we create a new instance of this class we need to pass in the sort expression and the sort direction. The compare method can then use reflection to obtain the value of the property passed in:
public class Comparer : IComparer
{
string _SortPropertyName;
int _SortDirection = -1; //asc = -1 desc = 1
public Comparer(string sortPropertyName, SortDirection sortDirection)
{
if (sortDirection == SortDirection.Descending)
this._SortDirection = 1;
this._SortPropertyName = sortPropertyName;
}
public int Compare(object x, object y)
{
object valueOfX = x.GetType().GetProperty(_SortPropertyName).GetValue(x, null);
object valueOfY = y.GetType().GetProperty(_SortPropertyName).GetValue(y, null);
IComparable comp = valueOfY as IComparable;
return ReverseResult(comp.CompareTo(valueOfX));
}
private int ReverseResult(int i)
{
return (i * (int)_SortDirection);
}
}
Info: IComparer is an interface which defines a single Compare method. The .NET Framework’s Array method will use this compare method to perform its sorting.
Now we have a new Compare class we can pass this to the overloaded sort method with the array. After the sort we rebind the GridView by calling the DataBind() method:
private void SortableGridView_Sorting(object sender, GridViewSortEventArgs e)
{
try
{
SortDirection sortDirection = GetSortDirection(e.SortExpression);
Array array = (Array)this.DataSource;
Array.Sort(array, new Comparer(e.SortExpression, sortDirection));
this.DataBind();
}
catch (Exception ex)
{
HttpContext.Current.Trace.Warn("GridView Sorting", ex.Message, ex);
}
}
To use our new Sortable GridView we need to add a reference to our CustomServerControls Library or copy and paste the .dll it produces into the bin folder at the root of your website. To add a reference right click the website project Add Reference > Projects Tab > Double click the CustomServerControls project.
Now we have a reference to the control we need to register the controls on the pages that require the sortable grid view like this:
<%@ Register Assembly="CustomServerControls" Namespace="CustomServerControls" TagPrefix="rma" %>
The assembly name and namespace can be obtained by right clicking on your CustomServerControls project and selecting properties. The TagPrefix can be anything you want!
Then to use it:
<rma:SortableGridView runat="server" ID="gvCustomers" AutoGenerateColumns="false" AllowSorting="true"> <Columns> <asp:BoundField DataField="Forename" HeaderText="Forename" SortExpression="Forename" /> <asp:BoundField DataField="Surname" HeaderText="Surname" SortExpression="Surname" /> </Columns> </rma:SortableGridView>
If you want to use your custom GridView on many pages you can register it once in the Web.config file under <system.web> like this:
<pages>
<controls>
<add assembly="CustomServerControls" namespace="CustomServerControls" tagPrefix="rma"/>
</controls>
</pages>
Download: SortableGridView.zip (43.09 kb)