GridView Multicolumn sorting


GridView Multicolumn sorting

The ASP.NET GridView control is the successor to the v1.x DataGrid, adding the ability to take advantage of specific capabilities of ASP.NET data source controls. Whereas the v1.x DataGrid required a page developer to write custom code to handle simple operations such as paging, sorting, editing or deleting data, the GridView control can automatically handle these operations provided its bound data source control supports these capabilities. The GridView also offers some functionality improvements over the DataGrid, such as the ability to define multiple primary key fields, and some UI customization improvements, such as new field types and templating options. It also exposes a new model for page developers to handle or cancel events. Read more..

GridView's built-in sorting can be enabled by setting a data source and AllowSorting property to true. Click on column header to sort based on the SortExpression specified in the Gridview column. However, Gridview does not support multi column sorting which is required in day to day application.

So here is how I tried to create one-

Create a custom control that will inherit from gridview

public class MCNGridView : GridView

Create properties to enable/disable multicolumn sorting

            
bool _ShowSortSequence = false;
        bool _EnableMultiColumnSorting = false;
        string _SortAscImageUrl = string.Empty;
        string _SortDescImageUrl = string.Empty;
        #region Properties
        /// <summary>
        /// Enable/Disable MultiColumn Sorting.
        /// </summary>
        [
        Description("Sorting On more than one column is enabled or not"),
        Category("Behavior"),
        DefaultValue("false"),
        ]
        public bool EnableMultiColumnSorting
        {
            get { return _EnableMultiColumnSorting; }
            set { AllowSorting = true; _EnableMultiColumnSorting = value; }
        }
        /// <summary>
        /// Enable/Disable Sort Sequence visibility.
        /// </summary>
        [
        Description("Show Sort Sequence or not"),
        Category("Behavior"),
        DefaultValue("false"),
        ]
        public bool ShowSortSequence
        {
            get { return _ShowSortSequence; }
            set { _ShowSortSequence = value; }
        }
        /// <summary>
        /// Get/Set Image for displaying Ascending Sort order.
        /// </summary>
        [
        Description("Image to display for Ascending Sort"),
        Category("Misc"),
        Editor("System.Web.UI.Design.UrlEditor", typeof(System.Drawing.Design.UITypeEditor)),
        DefaultValue(""),

        ]
        public string SortAscImageUrl
        {
            get { return _SortAscImageUrl; }
            set { _SortAscImageUrl = value; }
        }
        /// <summary>
        /// Get/Set Image for displaying Descending Sort order.
        /// </summary>
        [
        Description("Image to display for Descending Sort"),
        Category("Misc"),
        Editor("System.Web.UI.Design.UrlEditor", typeof(System.Drawing.Design.UITypeEditor)),
        DefaultValue(""),
        ]
        public string SortDescImageUrl
        {
            get { return _SortDescImageUrl; }
            set { _SortDescImageUrl = value; }
        }

Override Gridview OnSorting event

To get the custom sort expression

protected override void OnSorting(GridViewSortEventArgs e)
        {
            if (EnableMultiColumnSorting)
                e.SortExpression = GetSortExpression(e);
            base.OnSorting(e);
        }

Override Gridview OnRowCreated event

To show the sort direction image and sort sequence

protected override void OnRowCreated(GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.Header)
            {
                if (SortExpression != String.Empty)
                    ShowSortOrderImages(SortExpression, e.Row);
            }
            base.OnRowCreated(e);
        }


Other protected methods

            
/// <summary>
        ///  Get Sort Expression from existing Grid View Sort Expression
        /// </summary>
        protected string GetSortExpression(GridViewSortEventArgs e)
        {
            string[] sortColumns = null;
            string sortAttribute = SortExpression;
            if (sortAttribute != String.Empty)
            {
                sortColumns = sortAttribute.Split(",".ToCharArray());
            }
            if (sortAttribute.IndexOf(e.SortExpression) > 0 || sortAttribute.StartsWith(e.SortExpression))
                sortAttribute = UpdateSortExpression(sortColumns, e.SortExpression);
            else
                sortAttribute += String.Concat(",", e.SortExpression, " ASC ");
            return sortAttribute.TrimStart(",".ToCharArray()).TrimEnd(",".ToCharArray());

        }
        /// <summary>
        ///  Toggle the sort order or remove the column from sort
        /// </summary>
        protected string UpdateSortExpression(string[] sortColumns, string sortExpression)
        {
            string ascSortExpression = String.Concat(sortExpression, " ASC ");
            string descSortExpression = String.Concat(sortExpression, " DESC ");
            for (int i = 0; i < sortColumns.Length; i++)
            {
                if (ascSortExpression.Equals(sortColumns[i]))
                    sortColumns[i] = descSortExpression;
                else if (descSortExpression.Equals(sortColumns[i]))
                    Array.Clear(sortColumns, i, 1);
            }
            return String.Join(",", sortColumns).Replace(",,", ",").TrimStart(",".ToCharArray());
        }
        /// <summary>
        ///  Lookup the Current Sort Expression to determine the Order of a specific item.
        /// </summary>
        protected void SearchSortExpression(string[] sortColumns, string sortColumn, out string sortOrder, out int sortOrderNo)
        {
            sortOrder = "";
            sortOrderNo = -1;
            for (int i = 0; i < sortColumns.Length; i++)
            {
                if (sortColumns[i].StartsWith(sortColumn))
                {
                    sortOrderNo = i + 1;
                    if (EnableMultiColumnSorting)
                        sortOrder = sortColumns[i].Substring(sortColumn.Length).Trim();
                    else
                        sortOrder = ((SortDirection == SortDirection.Ascending) ? "ASC" : "DESC");
                }
            }
        }
        /// <summary>
        ///  Show an image for the Sort Order with sort sequence no.
        /// </summary>
        protected void ShowSortOrderImages(string sortExpression, GridViewRow dgItem)
        {
            string[] sortColumns = sortExpression.Split(",".ToCharArray());

            for (int i = 0; i < dgItem.Cells.Count; i++)
            {
                if (dgItem.Cells[i].Controls.Count > 0 && dgItem.Cells[i].Controls[0] is LinkButton)
                {
                    string sortOrder;
                    int sortOrderNo;
                    string column = ((LinkButton)dgItem.Cells[i].Controls[0]).CommandArgument;
                    SearchSortExpression(sortColumns, column, out sortOrder, out sortOrderNo);
                    if (sortOrderNo > 0)
                    {
                        string sortImgLoc = (sortOrder.Equals("ASC") ? SortAscImageUrl : SortDescImageUrl);
 
                        if (sortImgLoc != String.Empty)
                        {
                            Image imgSortDirection = new Image();
                            imgSortDirection.ImageUrl = sortImgLoc;
                            dgItem.Cells[i].Controls.Add(imgSortDirection);
                            if (EnableMultiColumnSorting && _ShowSortSequence)
                            {
                                Label lblSortOrder = new Label();
                                lblSortOrder.Font.Size = FontUnit.XSmall;
                                lblSortOrder.Font.Name = "verdana";
                                lblSortOrder.Text = sortOrderNo.ToString();
                                dgItem.Cells[i].Controls.Add(lblSortOrder);
                            }
                        }
                        else
                        {
                            Label lblSortDirection = new Label();
                            lblSortDirection.Font.Size = FontUnit.XSmall;
                            lblSortDirection.Font.Name = "verdana";
                            lblSortDirection.EnableTheming = false;
                            lblSortDirection.Text = (sortOrder.Equals("ASC") ? "^" : "v");
                            dgItem.Cells[i].Controls.Add(lblSortDirection);
                            if (EnableMultiColumnSorting && _ShowSortSequence)
                            {
                                Literal litSortSeq = new Literal();
                                litSortSeq.Text = sortOrderNo.ToString();
                                dgItem.Cells[i].Controls.Add(litSortSeq);
                            }
                        }
                    }
                }
            }
        }

On Aspx page

Register the control

<%@ Register Assembly="MCNGridView" namespace="MCNGridView" tagprefix="MCN" %>

Create the grid and bind the data

<MCN:MCNGridView ID="MCNGridView1" runat="server" EnableMultiColumnSorting="True"       
        AutoGenerateColumns="False" DataKeyNames="CustomerID"  ShowSortSequence="True"
        DataSourceID="SqlDataSource1" CellPadding="4"
        ForeColor="#333333" GridLines="None" AllowPaging="True"
        SortAscImageUrl="~/Images/asc.png" SortDescImageUrl="~/Images/desc.png">
        <RowStyle BackColor="#EFF3FB" />
        <Columns>
            <asp:BoundField DataField="CustomerID" HeaderText="CustomerID" ReadOnly="True"
                SortExpression="CustomerID">
            </asp:BoundField>
            <asp:BoundField DataField="CompanyName" HeaderText="CompanyName"
                SortExpression="CompanyName" />
            <asp:BoundField DataField="ContactName" HeaderText="ContactName"
                SortExpression="ContactName" />
            <asp:BoundField DataField="ContactTitle" HeaderText="ContactTitle"
                SortExpression="ContactTitle" />
            <asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
            <asp:BoundField DataField="Region" HeaderText="Region"
                SortExpression="Region" />
            <asp:BoundField DataField="PostalCode" HeaderText="PostalCode"
                SortExpression="PostalCode" />
            <asp:BoundField DataField="Country" HeaderText="Country"
                SortExpression="Country" />
        </Columns>
        <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
        <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
        <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
        <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
        <EditRowStyle BackColor="#2461BF" />
        <AlternatingRowStyle BackColor="White" />
    </MCN:MCNGridView>

Thats all you are ready with Multicolumn sorting in Gridview.