1. Original Entry + Comments2. Write a Comment3. Preview Comment
New comments for this entry are disabled.


February 01, 2006  |  Custom bi-directional sorting in GridView  |  89013 hit(s)

One of the things I like is how as you work with ASP.NET (or anything), your instincts develop a bit and you can figure out how to do stuff with only a few dents in your forehead where you've been banging it against the keyboard.

I have an app that uses a GridView control to display some information. The GridView control includes sorting (for free), which is bidirectional (also for free). First you set AllowSorting[1] to true, them for each column (bound or templated), you set a sort expression. This renders a LinkButton in the column header, and the grid toggles between sorting ascending and descending per your sort expression. Nice.

For various reasons, in one of my apps I have to do the bi-directional part manually. But this isn't very hard at all. First, in the GridView column, I specified a custom sort expression (that is, an expression that doesn't map directly to a database field):
<asp:TemplateField HeaderText="Date" SortExpression="datesort">
<ItemTemplate>
<asp:Label ID="labelPDate" runat="server" Text=""></asp:Label>
</ItemTemplate>
</asp:TemplateField>
In the page code, I created a DateSortDirection property, and I initialize it in the Page_Load handler.
Public Property DateSortDirection() As String
Get
Return ViewState("DateSortDirection")
End Get
Set(ByVal value As String)
ViewState("DateSortDirection") = value
End Set
End Property


Protected Sub Page_Load()
If Not Page.IsPostBack Then
Me.DateSortDirection = "ASC"
End If
End Sub
To implement bidirectional sorting, I handle the grid's Sorting handler:
Protected Sub GridView1_Sorting(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.GridViewSortEventArgs)
If e.SortExpression = "datesort" Then
If Me.DateSortDirection = "ASC" Then
e.SortExpression = "YEAR DESC, MONTH DESC, DAY DESC"
Me.DateSortDirection = "DESC"
Else
e.SortExpression = "YEAR ASC, MONTH ASC, DAY ASC"
Me.DateSortDirection = "ASC"
End If
End If
End Sub
As an added touch, I wanted to display an arrow in the header to indicate which direction the sort was:



I created two little .gif files -- an up arrow and a down arrow. To display them in the header, I dynamically added an Image control (plus a space) to the appropriate column of the header, like this:
Protected Sub GridView1_RowDataBound(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.Header Then
Dim img As New Image
If Me.DateSortDirection = "ASC" Then
img.ImageUrl = "asc.gif"
Else
img.ImageUrl = "desc.gif"
End If
e.Row.Cells(0).Controls.Add(New LiteralControl(" "))
e.Row.Cells(0).Controls.Add(img)
End If
End Sub
And that was it. Works great!

[1] Per our style guide, this should be EnableSorting.


      Se você como eu usa diversas fontes de dados e não fixa uma fonte só no...



Minh   02 Feb 06 - 10:53 AM

I seem to recall writing a sample for the ref docs that did this. I can't for the life of me find it now, but I think I used the GridView.SortDirection property to determine the sort order. That way, you don't need to keep track of sort order yourself. GridView makes it even easier. :-)

 
Minh   02 Feb 06 - 11:01 AM

Duh! I guess I should have looked at the docs for GridView.SortDirection property: http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.sortdirection.aspx

 
mike   07 Feb 06 - 3:04 AM

>GridView.SortDirection

True. I will note, however, that by managing the sort direction property yourself, you can create a, uh, multidirectional sort. By which I mean, you can set it up so that clicking the column header doesn't just toggle between up and down; it could toggle various combinations of ASC and DESC. An actually useful version of that might be to take into account the last known sort in other columns. For example: I sort by name. Now I sort by date, but the grid is smart enough to use name as a secondary sort. Etc.

A stretch, I realize. :-)


 
David Reed   08 Feb 06 - 11:27 PM

Why would you set the DateSortDirection to "ASC" in page load like that, when you could just return "ASC" as the default value?

Why you ask? Because then the value "ASC" won't be persisted into viewstate on the rendered form. Yeah you're only saving a whopping 3 characters of viewstate data but that translates to more than 3 bytes of encoded viewstate, plus its just the right way to do it in general :). ViewState is only supposed to store CHANGES to the forms default state but in your case it is storing the default value too.

I'm most familiar with C# so I'll demonstrate with that (I dont know if vb has a ternary operator?)
public string DateSortDirection
{
get { return ViewState["DateSortDirection"] == null ? "ASC" : ViewState["DateSortDirection"] as string; }
set { ViewState["DateSortDirection"] = value; }
}
Then no code in Page_Load is necessary!


 
Adrian Hara   14 Feb 06 - 1:05 AM

As an alternative to using two arrow images, you could display a label, with the webdings font, to get the up/down arrows, like so:

Label lbl = new Label();
lbl.Font.Name = "Webdings";
if (this.IsSortedAscending)
{
lbl.Text = "5";
}
else
{
lbl.Text = "6";
}


 
mike   14 Feb 06 - 1:10 AM

Do you happen to know if that's supported on all platforms? (e.g. on Safari on the Mac, etc.?)

 
Adrian Hara   14 Feb 06 - 7:51 AM

It doesn't work on Safari 2.0 unfortunately :(, didn't/couldn't test on any other versions of Safari.

And btw, the tip is not mine, i've seen it in an issue of MSDN Magazine a long time ago :)


 
mark   15 Mar 06 - 12:49 PM

You could also use a CssClass instead of having to add the image control -- using the background: url()


 
ralph   10 Apr 06 - 3:44 PM

Mike,

Displaying the sort direction in the column of GridView using an image or Webdng font is great. Problem though, on a postback of this page, the sort direction does not display. So it is limited, since if the user navigates between pages, the sort direction indicator disappears.

Any easy way to fix this on postback?


 
mike   06 May 06 - 11:19 AM

Ralph -- I am not able to repro the problem with the sort indicator disappearing on postback. You can see it in action here:

http://mikepope.com/marcus/performances.aspx

(I could easily believe, however, that I've left out some important little piece of information.)


 
DK   13 May 06 - 12:11 PM

do you know how to enable sorting if I use HeaderTemplate instead of HeaderText? I've tried LinkButton but without success. thanks.

btw on your performances.aspx sample i see the arrow image always on 1st column.


 
mike   14 May 06 - 2:32 PM

One possibility would be to use a LinkButton control and set its CommandName property to some custom sort string. Handle the GridView control's RowCommand event and within that event do the custom sort. (?)

>btw on your performances.aspx sample i see the arrow image always on 1st column.

I only implemented it on the first column for some reason.


 
nick   02 Aug 06 - 11:52 AM

why not just something like this...

protected void grid_Sorted(object sender, EventArgs e)
{
foreach (DataControlField field in grid.Columns)
{
field.HeaderStyle.CssClass = "";
if (grid.SortExpression != "" && field.SortExpression == grid.SortExpression)
{
field.HeaderStyle.CssClass = grid.SortDirection == SortDirection.Ascending ? "DownArrow" : "UpArrow";
}
}
}


 
david   25 Aug 07 - 4:09 AM

Thank you Mike, you have helped me a lot with this short piece of code.
it is simple and works like a charm.