Monday, June 15, 2009

The GridView 'GridView1' fired event Sorting which wasn't handled.

If you get the error:

The GridView 'GridView1' fired event Sorting which wasn't handled.

You are likely using an ObjectDataSource and then set AllowSorting to true or you are binding to directly your GridView in Page_Load using something like this.

It means you using a GridView that has AllowSorting=”true” equal to true and for some reason nothing has told it what will handle the sorting. The easiest way to avoid this is to use a DataSource control such as an EntityDataSource, SqlDataSource, or LinqDataSource control. The ObjectDataSource will not help you out of the box though. Some extra stuff is required. I’ll show you that later.

This page entry is broken up into to sections. One if you are binding directly to the GridView in your page load and thus have no DataSource assigned to the GridView. Another if you have are using an ObjectDataSource.

In both sections I assume you are using LINQ to access the database, but you could use anything to talk to the database. The logic that needs to be implemented is still the same. I also assume you have an object that encapsulates your database access (a Database Access Layer (DAL)).

For this example, let’s assume you have used the ADO.NET Entity Data Model in Visual Studio to create your entities. In this example we have one entity called Person. It has 3 properties: ID, FName, LName.

Data Access Layer

Below is a solution if you are using an LINQ to Entities, though it would be virtually identical to LINQ to SQL. A similar solution could be used for SQL, though in that case you would translate the requests to SQL statements.
public class DAL
{
private MyEntities ctx = new MyEntities();

public IQueryable GetPerson(string sortExpression)
{
// set a default sort order for when the page is first rendered
if (string.IsNullOrEmpty(sortExpression))
{
sortExpression = "FName Descending";
}

var people = from p in ctx.Person
select p;


var sortedPeople = people.OrderBy(sortExpression)
.Select("new(ID, FName, LName)");

return sortedPeople;

}}

You may notice that the .OrderBy() method gives you a compiler error or is not in your Intellisense. You need to download it from Microsoft. Click here to download. In the zip file, look for the Dynamics.cs file. You can include it in your project or you can build the project that comes with and include the assembly it builds in your project. It is one file, so I like putting it in my project as source code.
This Dynamic class works very in scenarios like this because it actually supports the same syntax as the ObjectDataSource uses which is <ColumnName> <SortDirection>. If the sort direction is Ascending, then no direction is specifed by the ObjectDataSource. For example the syntax for sorting my FName in Ascending order, the sortExpression would be “FName” or “FName Descending” if you wanted to sort in Descending order.
The GridView sortingEvent also uses very similar syntax. In either case, this saves us from writing a bunch of if-else or switches for each column and sort direction. The choice is yours. This is just so easy, and it is clean.

Binding ObjectDataSource to GridView


This is by far easier of the two methods. I highly recommend using a DataSource such as the ObjectDataSource. The code is much simpler.
To make the ObjectDataSource sort all you have to do is set the DataSourceID property on the GridView to the ID of your ObjectDataSource.
You do have to tell the ObjectDataSource some things about your Data Access Layer though. You need to tell it the type for your Data Access Layer, the method to call, and what the parameter name is for sortExpression the GridView will pass you.
Here is my ObjectDataSource that I defined for the Data Access Layer we defined above.

<asp:ObjectDataSource ID="dsContracts" runat="server" 
SelectMethod="GetPerson"
TypeName="DataModel.DAL"
SortParameterName="sortExpression"
>
</asp:ObjectDataSource>

Binding Directly to GridView in Page Load

If you want to work a little harder you can implement the logic using the GridView and no ObjectDataSource. If you are bind directly to your GridView in your page load, all you to do to stop this error message is handle the Sorting event on your GridView. While this stops the error message, it doesn’t give you sorting when a column header is clicked. You need to put some logic in the Sorting event for it to do something useful.

You are likely binding your DAL to your GridView using something like this or maybe conditionally if it isn’t a postback:

protected void Page_Load(object sender, EventArgs e)
{
GridView1.DataSource = new DAL().GetPerson("");
GridView1.DataBind();
}
The GridView does NOT set the SortDirection property in this event handler unless an DataSource object is set. This means that Sorting event ALWAYS will have a e.SortDirection equal to SortingDirection.Ascending. This is a bug in my mind, but I think Microsoft just says it is by design (or bad design if you ask me). For more explanation on this please see here for the response from Microsoft.
As a recommended workaround, we need to track the SortDirection ourselves. In order to do something useful, we need to also track the column that was clicked so that we know when to reset the sort direction to the default direction.
Here is the code to handle the sorting event for GridView. Be sure to wire it up to your GridView.
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{

// get values from viewstate
String sortExpression = ViewState["_GridView1LastSortExpression_"] as string;
String sortDirection = ViewState["_GridView1LastSortDirection_"] as string;

// on first time header clicked ( including different header), sort ASCENDING
if (e.SortExpression != sortExpression)
{
sortExpression = e.SortExpression;
sortDirection = "Ascending";
}
// on second time header clicked, toggle sort
else
{
if (sortDirection == "Ascending")
{
sortExpression = e.SortExpression;
sortDirection = "Descending";
}
// Descending
else
{
sortExpression = e.SortExpression;
sortDirection = "Ascending";
}
}

// save state for next time
ViewState["_GridView1LastSortDirection_"] = sortDirection;
ViewState["_GridView1LastSortExpression_"] = sortExpression;

// NOTE: Depending on the syntax you require for your sortExpression parameter
// to your method, you may need to convert the sort expression to that syntax.
GridView1.DataSource = new DAL().GetPerson(sortExpression + " " + sortDirection);
GridView1.DataBind();
}

7 comments:

Anonymous said...

This is nice technique. You can also use a Data Access Layer that simply returns a DataTable:

public class DAL
{
public DataTable GetPerson()
{
return HttpContext.Current.Session["myPersonTable"] as DataTable
}
}

Then, instead of putting SortExpression on the objectDataSource, include it on your databound fields in the Gridview. You then get column sorting (both ascending and descending) without additional code-behind.

Pablo Alejandro PĂ©rez Acosta said...

Thank you very much!

Roman said...

When I do binding inside PostBack check it does not work. Paging makes touble and so does command inside gridview

tri said...

Very good one..
Will you please also guide on how apply sorting in header template..

My template field is like this


















In this the sort expression



doesn't work like others without header template..

What is the technique for this.

Anonymous said...

GridView sorting in C#.NET

lee woo said...

Winners make a habit of manufacturing their own positive expectations in advance of the event. See the link below for more info.


#event
www.ufgop.org

Silvia Jacinto said...

I really love your blog there's a lot to share. Keep it up.Visit my site too.

n8fan.net

www.n8fan.net