Clickable GridView Headers - Raj Kaimal

Clickable GridView Headers

By default, you can sort on the columns of a GridView by clicking on the LinkButton in the header of a column that has a SortExpression defined. This post is going to attempt three things.

  1. Sort the column by clicking anywhere in the header cell
  2. Add a sort indicator to cells that are sortable. Cells that are sortable will have a "neutral" icon. Note in the screen capture below that UnitsInStock does not have a "neutral" icon because it does not have a SortExpression defined.
  3. Highlight the column of data that is being sorted.

gridview_clickable_headers

The idea is to add an onclick attribute that calls a javascript function to all GridView header cells that are sortable.

In the javscript function, we determine which element raised the event. If the element that raised the event is not the LinkButton, we find the LinkButton and invoke its onclick or href attribute method based on the GridView EnableSortingAndPagingCallbacks property. When the EnableSortingAndPagingCallbacks is set to true, we invoke the onclick method otherwise we invoke the method the href attribute points to.

We are checking which element raised the event in the javscript function to prevent the postback or callback from being called twice. This is because, without this check, clicking on the LinkButton will fire the method as well as bubble the event up to its parent cell causing it to be fired again (since we have added an onclick attribute to the cell). The javascript function calls the postback or callback method only if the element that raised the event is not the LinkButton.

    public void MakeGridViewHeaderClickable(GridView gridView, GridViewRow gridViewRow) {

        if (gridViewRow.RowType == DataControlRowType.Header) {

            for (int i = 0; i < gridView.Columns.Count; i++) {

                string sortExpression = gridView.Columns[i].SortExpression;

                TableCell tableCell = gridViewRow.Cells[i];

 

                //Make sure the column we are working with has a sort expression

                if (!string.IsNullOrEmpty(sortExp ression)) {

                    //Enumerate the controls within the current cell and find the link button.

                    foreach (Control gridViewRowCellControl in gridViewRow.Cells[i].Controls) {

 

                        LinkButton linkButton = gridViewRowCellControl as LinkButton;

 

                        if ((linkButton != null) && (linkButton.CommandName == "Sort")) {

 

                            //Add an onclick attribute to the current cell

                            tableCell.Attributes.Add("onclick", "RequestData('" + linkButton.ClientID + "', this, event)");

 

                            tableCell.Style.Add(HtmlTextWriterStyle.Cursor, "hand");

                            tableCell.Style.Add(HtmlTextWriterStyle.Cursor, "pointer");

                            break;

                        }

                    }

                }

            }

        }

    }
 

    protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e) {

        MakeGridViewHeaderClickable(GridView1, e.Row);

    }

The javascript function is shown below

    <script language="javascript" type="text/javascript">

    function RequestData(linkId, cellElement, evt) {

        var evtSource;

 

        evt = (evt)? evt : window.event;

        evtSource = (evt.srcElement)? evt.srcElement : evt.target;

 

        //When a hyperlink is clicked, Safari returns the text node as the source element rather

        //than the hyperlink. parentNode will give us the hyperlink element.

        //ref: http://developer.apple.com/internet/webcontent/eventmodels.html

        if (evt.target) {

            if (evt.target.nodeType == 3) {

                evtSource = evtSource.parentNode;

            }

        }

 

        //If event was raised from an element other than the LinkButton

        if ((evtSource.getAttribute("id") != linkId) && (evt.type == "click")) {

 

            //Get a collection of "a" tags inside the cell       

            var linkCollection = cellElement.getElementsByTagName("a");

            for (var i = 0; i < linkCollection.length; i++) {

 

               //If the link button has an onclick attribute, call the onclick.

               //The onclick attribute is present when the GridView is using callback

               //example: onclick="java script:__gvGridSort1_GridView1.callback(...); return false;"

               var onClickAttribute = linkCollection[i].getAttribute("onclick");

               if (onClickAttribute != null) {

                linkCollection[i].onclick();

                break;

               }

 

               //If the link button has a href attribute, set the location of the page

               //to the href value.

               //The href attribute is used when the GridView is not using callbacks

               //example: href="java script:__doPostBack('GridSort1$GridView1','Sort$UnitsOnOrder')"

               var hrefAttribute = linkCollection[i].getAttribute("href");

               this.location.href = hrefAttribute;

               break;

            }

        }

    }

 

    </script>

If you run the above code, you will notice that it is different from screen capture above because the sort image and column highlighting are missing. To implement this, we need to add some code to the MakeGridViewHeaderClickable method as shown below.

    public void MakeGridViewHeaderClickable(GridView gridView, GridViewRow gridViewRow) {

 

        if (gridViewRow.RowType == DataControlRowType.Header) {

 

            for (int i = 0; i < gridView.Columns.Count; i++) {

 

                string sortExpression = gridView.Columns[i].SortExpression;

                TableCell tableCell = gridViewRow.Cells[i];

 

                //Make sure the column we are working with has a sort expression

                if (!string.IsNullOrEmpty(sortExp ression)) {

 

                    System.Web.UI.WebControls.Image sortDirectionImageControl;

 

                    //Create an instance of a Image WebControl

                    sortDirectionImageControl = new System.Web.UI.WebControls.Image();

 

                    //Determine the image url based on the SortDirection

                    string imageUrl = "~/Images/sort_neutral.gif";

                    if (sortExpression == gridView.SortExpression) {

                        imageUrl = (gridView.SortDirection == SortDirection.Ascending) ?

                            "~/Images/sort_asc.gif" : "~/Images/sort_desc.gif";

                    }

 

                    //Add the Image Web Control to the cell

                    sortDirectionImageControl.ImageUrl = imageUrl;

                    sortDirectionImageControl.Style.Add(HtmlTextWriterStyle.MarginLeft, "10px");

                    tableCell.Wrap = false;

                    tableCell.Controls.Add(sortDirectionImageControl);

 

                    //Enumerate the controls within the current cell and find the link button.

                    foreach (Control gridViewRowCellControl in gridViewRow.Cells[i].Controls) {

 

                        LinkButton linkButton = gridViewRowCellControl as LinkButton;

 

                        if ((linkButton != null) && (linkButton.CommandName == "Sort")) {

 

                            //Add an onclick attribute to the current cell

                            tableCell.Attributes.Add("onclick", "RequestData('" + linkButton.ClientID + "', this, event)");

 

                            tableCell.Style.Add(HtmlTextWriterStyle.Cursor, "hand");

                            tableCell.Style.Add(HtmlTextWriterStyle.Cursor, "pointer");

                            break;

                        }

                    }

                }

            }

        }

 

        //Enuerate all the rows and change the color of the column that is being sorted

        if (gridViewRow.RowType == DataControlRowType.DataRow) {

 

            for (int i = 0; i < gridViewRow.Cells.Count; i++) {

                if ((!String.IsNullOrEmpty(gridView.SortExpr ession)) &&

                   (gridView.Columns[i].SortExp ression == gridView.SortExp ression)) {

                    Color sortColumnBgColor;

 

                    sortColumnBgColor = (gridViewRow.RowState != DataControlRowState.Alternate) ?

                        ColorTranslator.FromHtml("#d7e3f1") : ColorTranslator.FromHtml("#f7fbff");

 

                    gridViewRow.Cells[i].BackColor = sortColumnBgColor;

                }

            }

        }

    }

You can also use this with HeaderTemplates. Just make sure your HeaderTemplate contains a LinkButton with a CommandName attribute set to "Sort" as shown below:

<asp:TemplateField HeaderText="ProductID" SortExpression="ProductID">

    <ItemTemplate>

        <asp:Label ID="Label1" runat="server" Text='<%# Bind("ProductID") %>'></asp:Label>

    </ItemTemplate>

    <HeaderTemplate>

        <asp:LinkButton runat="server" CommandName="Sort" CommandArgument="ProductID"

            Text="ProductID"></asp:LinkButton>

    </HeaderTemplate>

</asp:TemplateField>

You can easily make these transparent sort icons by working at the pixel level ( I used Photoshop)
sort_asc sort_desc sort_neutral

The MakeGridViewHeaderClickable method can be placed in a Utility class if you so desire. A reference to the GridView can be obtained by using gridViewRow.Parent.Parent or (GridView) gridViewRow.NamingContainer if you don't want to pass in a GridView reference to the method.

Tested on IE 6.0.2900, Firefox 1.5.0.6 and Safari 1.3.2

Note: I had to add a space in some variables because Community Server (running this blog) was blocking my scripts. Example: sortExp ression.

 

Published Friday, August 4, 2006 11:40 AM by rajbk
Filed under: ,

Comments

# re: Clickable GridView Headers

I prefer to define sort images in CSS.  Just define different CSS classes for cells which are neutral/ascending/descending, and the CSS can define a background image.  This gives more control to the page designer.

Saturday, August 5, 2006 3:34 AM by Joe

# re: Clickable GridView Headers

I think this is a good and clear article,

very useful.

Monday, August 7, 2006 6:02 AM by Alex

# re: Clickable GridView Headers

This is highly appreciable and very good article

Wednesday, January 17, 2007 6:44 AM by Pramod

# re: Clickable GridView Headers

This was extremely helpful thanks for posting!!

Monday, January 22, 2007 2:53 PM by RP

# re: Clickable GridView Headers

it looks awesome..

im just having a problem,

if i add my columns programmatically...

eg. DataTable dt = new DataTable();

   DataColumn dcol = new DataColumn("DocID", typeof(System.String));

   dt.Columns.Add(dcol);

when i do a GridView1.Columns.Count... it doesnt list these columns. thus i cant use this header code. anyone know why?!?!

thanks.

Tuesday, April 24, 2007 2:07 AM by Dan

# re: Clickable GridView Headers

When I click on Next or Previous button the value of Page Index in GridView1.PageIndex = e.NewPageIndex; always remains 1 and because of this previous and next button are not working.

First result, Last result and by selecting the value from dropdown list are working fine.

PLEASE HELP!!!!!!!!!

Wednesday, June 13, 2007 12:52 PM by Laxi

# re: Clickable GridView Headers

Excellent code, thanks a lot!

Monday, October 8, 2007 7:25 AM by Marnix

# re: Clickable GridView Headers

excellent article!

Thursday, October 18, 2007 6:04 AM by Tony Qu

# re: Clickable GridView Headers

Thanks!!! It was worth implementing

Friday, November 2, 2007 10:32 AM by Munish

# re: Clickable GridView Headers

What an elegant solution! Thank you!

I cannot believe how hobbled Microsoft made the GridView, and that this functionality is not built in.

Wednesday, December 12, 2007 3:06 PM by Joe G

# re: Clickable GridView Headers

excellent code,the javascript code is too good.Hats off to the developer

Wednesday, December 26, 2007 3:34 AM by sachi

# re: Clickable GridView Headers

Thanks buddy...

its really helped me....

Wednesday, March 26, 2008 11:10 AM by ASP.NET Guy

# re: Clickable GridView Headers

hi

i hav done wht hav written in ur code

but its not working.......

i tried to impliment sorting it calls the

javascript function but does not perform any action i mean no sorting is done

Tuesday, April 8, 2008 3:08 AM by ruchika

# re: Clickable GridView Headers

hi

i hav done wht hav written in ur code

but its not working.......

i tried to impliment sorting it calls the

javascript function but does not perform any action i mean no sorting is done

Wednesday, August 6, 2008 10:32 AM by ruchika

# re: Clickable GridView Headers

Good one, works for me...

Monday, September 29, 2008 12:13 AM by maheshsingh

# re: Clickable GridView Headers

Is this example really works? i m getting error when using with template field. "turn off call back".

Friday, March 13, 2009 6:33 AM by vijayvishwakarma

# re: Clickable GridView Headers

Thanks dear it is too good and awsome.

Thursday, June 25, 2009 9:02 AM by Yaser

# re: Clickable GridView Headers

Very good article!! Excelent work!!

Wednesday, November 4, 2009 5:45 PM by Pierre Maroun

# re: Clickable GridView Headers

The article is not working for me....

No Sorting is done...

Do i have to enable the 'EnableSortig&Pagingcallback' property..?

Monday, January 4, 2010 7:07 AM by Santosh

# re: Clickable GridView Headers

Hi ive used this and it works find on  BoundField

Cant it how ever work on a combination of BoundField and TemplateField with label ?

cant seem to get it to work on anthing other than boundfield

 <asp:TemplateField HeaderText="From / To" HeaderStyle-HorizontalAlign="Center">

                                           <ItemTemplate>

                                               <asp:Label ID="lblToFromAcc" runat="server" Text='<%# SetToFromAcc(Eval("SourceAccountID"), Eval("DestinationAccountID")) %>' CssClass="cssGrdStyle_InmateReport"></asp:Label>

                                           </ItemTemplate>

                                           <ItemStyle HorizontalAlign="Left" />

                                       </asp:TemplateField>

Thursday, August 12, 2010 6:54 AM by urby

# re: Clickable GridView Headers

I am using the same code but am not able to get sorting data is it i need to add any code for sorting here.

pelase can u send me the complete code for Gridview Clickble Header Column.

 

Tuesday, October 26, 2010 2:55 AM by Murali