ASP.NET GridView: Multiple Column Sort

The native ASP.NET GridView allows for the sorting of data by setting the Grid's AllowSorting property to True.  With AllowSorting enabled, header row cells contain hyperlinks, which when clicked, sort the GridView.  There are several GridView properties and events available which can be used for the customization of the GridView's sort.

  • The Sort method, defined as Sort ( string sortExpression, SortDirection sortDirection ), sorts the GridView based upon the specified sort expression and direction.
  • The Sorting event occurs when the hyperlink column heading is clicked, but before the GridView has handled the sorting of the data.
  • The Sorted event occurs when the hyperlink column heading is clicked, but after the GridView has sorted the datasource.
  • The RowCreated event occurs when a row is created in the GridView.
  • The SortDirection property gets the sort direction of the column being sorted.
  • The SortExpression property gets the sort expression associated with the column being sorted.



The default sort action of the GridView sorts only by a single column.  Using the available sort related events and properties it is rather easy to create a GridView that can be sorted by multiple columns.  The following example is for a GridView than can be sorted by multiple columns.  In this example the columns are sorted in the order they are clicked by a tri-state function.  Clicking on a column heading will set a columns sort state to be Ascending, Descending or Off.  The columns in the GridView are sorted based upon in which they are click or turned on.  When the sort state of a column is Off it is removed from the sort.

using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;

/// <summary>
/// MySortItem is a class that is used to in a List to store the sort columns and thier order
/// </summary>
public class MySortItem
{
    private string columnname;
    public string ColumnName
    {
        get
        {
            return columnname;
        }
        set
        {
            columnname = value;
        }
    }
    private SortDirection columnsort;
    public SortDirection ColumnSort
    {
        get
        {
            return columnsort;
        }
        set
        {
            columnsort = value;
        }
    }
    public string SortToString( SortDirection cs )
    {
        string s = ( cs == SortDirection.Ascending ) ? "asc" : "desc";
        return s;
    }

}

public partial class _Default : System.Web.UI.Page
{
    DataSet ds = new DataSet ();
    // List for holding sorted columns, their order and their direction
    List<MySortItem> sl;

    protected void Page_Load( object sender, EventArgs e )
    {
        // If the Column Sort List does not exist, create it. the list is stored in a session variable
        if ( Session["sortitems"] == null )
        {
            Session["sortitems"] = new List<MySortItem> ();
        }
        sl = (List<MySortItem>) Session["sortitems"];

        if ( !Page.IsPostBack )
        {
            // Procedure for binding the datasource with the DataGrid
            BindDataGrid ( GridView1 );
        }
    }

    /// <summary>
    /// Called from within the application to Bind the DataGrid to a datasource. in this cases it is the default view of a table. 
    /// </summary>
    /// <param name="gv"></param>
    protected void BindDataGrid( GridView gv )
    {
        // Read data from an XML file
        ds.ReadXml ( Server.MapPath ( "~/App_Data/Data.xml" ) );

        // build the sort expression
        string sortexp = "";
        for ( int i = 0; i < sl.Count; i++ )
        {
            sortexp = ( i == 0 ) ? sl[i].ColumnName + " " + sl[i].SortToString ( sl[i].ColumnSort ) : sortexp + "," + sl[i].ColumnName + " " + sl[i].SortToString ( sl[i].ColumnSort );
        }

        //Set label text to display the sort expression on the page for the user
        Label3.Text = sortexp;
        // set the datasource's table default view to indicate sorting; a datatable could also be used directly. In this example a datasource
        // was used due to dynmaic XML data loading
        ds.Tables[0].DefaultView.Sort = sortexp;
        // set the datagrid's datasource
        gv.DataSource = ds.Tables[0].DefaultView;
        gv.DataBind ();
    }

    /// <summary>
    /// Assigned to the GridView's PagIndexChanging event. This is used if the GridView allows for paging. 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void GridView1_PageIndexChanging( object sender, GridViewPageEventArgs e )
    {
        try
        {
            if ( sender is GridView )
            {
                ( (GridView) sender ).PageIndex = e.NewPageIndex;
                BindDataGrid ( GridView1 );
                e.Cancel = false;
            }
        }
        catch
        {

            e.Cancel = true;
        }
    }

    /// <summary>
    /// Assigned to the GridView's Sorting event. 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void GridView1_Sorting( object sender, GridViewSortEventArgs e )
    {
        try
        {
            // Scroll through the list and see if the clicked GridView column exists in the sort list.
            // If there is an entry for the column then change the order or remove the column from the list (tri-state)
            bool found = false;
            for ( int i = 0; i < sl.Count; i++ )
            {
                if ( sl[i].ColumnName == e.SortExpression.ToString () )
                {
                    found = true;
                    switch ( sl[i].ColumnSort )
                    {
                        case SortDirection.Descending:
                            sl[i].ColumnSort = SortDirection.Ascending;
                            break;
                        case SortDirection.Ascending:
                            sl.RemoveAt ( i );
                            break;
                    }
                }
            }
            if ( !found )
            {
                MySortItem si = new MySortItem ();
                si.ColumnName = e.SortExpression.ToString ();
                si.ColumnSort = SortDirection.Descending;
                sl.Add ( si );
            }

            BindDataGrid ( GridView1 );
            //accept column sort
            e.Cancel = false;
        }
        catch ( Exception )
        {
            //error so cancel
            e.Cancel = true;
        }
    }

    /// <summary>
    /// Assigned to the GridView's RowCreated event.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void GridView1_RowCreated( object sender, GridViewRowEventArgs e )
    {
        // add images to the column header to indicate sort of each column
        if ( e.Row.RowType == DataControlRowType.Header )
        {
            foreach ( TableCell tc in e.Row.Cells )
            {
                if ( tc.HasControls () )
                {
                    // search for the header link
                    LinkButton lnk = (LinkButton) tc.Controls[0];
                    if ( ( lnk != null ) & ( sl != null ) )
                    {
                        // Get the position of the column in the List 
                        int i = sl.FindIndex ( delegate ( MySortItem s )
                        {
                            return s.ColumnName == lnk.CommandArgument;
                        } );
                        // if the column is found in the list
                        if ( i >= 0 )
                        {
                            System.Web.UI.WebControls.Image img = new System.Web.UI.WebControls.Image ();
                            //set the appropriate image
                            img.ImageUrl = "~/images/" + ( sl[i].ColumnSort == SortDirection.Ascending ? "asc" : "desc" ) + ".gif";
                            // add space and the sort image to header
                            if ( sl[i].ColumnName == lnk.CommandArgument )
                            {
                                tc.Controls.Add ( new LiteralControl ( " " ) );
                                tc.Controls.Add ( img );
                            }
                        }
                    }
                }
            }
        }
    }
}
This is all it takes to have an ASP.NET GridView that can be sorted by multiple columns.



   

Update a control on a Master Page

Creating a Master Page in Visual Studio makes simplifies the development of ASP.NET websites. Master Page's allow for the creation of consistent and standard layouts (template) for a group of pages or an entire website. ContentPlaceHolders are used to designate where on a page based on a Master Page will have variable content. Sometimes it may be necessary to update a control that is part of the Master Page, rather within the ContentPlaceHolder area. There are several ways to update a control on a Master Page. A page's Master property allows for access to a page’s Master Page content. The following code is one example of how to update a control on a master page. The code would be run from the page based on a Master Page.

Image MasterLogo = (Image) Master.FindControl ( "Logo" );
        if ( MasterLogo != null )
        {
            MasterLogo.ImageUrl = "~/images/logo.gif";
        }

        Label MasterLabel = (Label) Master.FindControl ( "Label1" );
        if ( MasterLabel != null )
        {
            MasterLabel.Text = "Set from page";
        }



   

Who's Addicted to Farming?

I have to admit it, I am.....

I am not one that is heavy into games or the whole social networking scene, but I have to say zynga did it right with their FarmVille game that is available for play on FaceBook. FarmVille allows you to grow fruits, vegetables and grains on your very own farm.  You purchase and plant seeds and then after a certain period of time you harvest your crops for money. Each crop ripens at different rates of time. Don't leave your ripe crops unattended for too long or they'll wilt away (the rate at which crops wilt is equal to the time they take to ripen). You are not only limited to harvesting crops, but you can harvest trees and have your very own farm animals. Your FarmVille farm can also be decorated with items that you purchase or gifts from your neighbors.

Point blank, the game is addicting. No body knows why, or how, such a simple game can be so time-consuming. Finally everyone, from children to grown adults, can get that chance to live life in the old days. Unlike other simple games, this role-playing simulation is fun overtime instead of just one session. Once you've got a neighbor, that's where the fun begins. Not only can you compete against them in terms of experience level, you can also bring it to a cosmetic level. Whose farm looks the best? Who's the better, more experienced farmer? This is where the game begins to resemble a drug. You'll literally find yourself cutting time out of your day just to plant more crops!

I have created some reference tables for those of you that are also addicted to farming.  If you would like to see any additional information on the page please let me know.

Check out the FarmVille Reference Tables page.