This is a migrated thread and some comments may be shown as answers.

Implementing a Generic Custom Sort Handler

1 Answer 83 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Stuart Hemming
Top achievements
Rank 2
Stuart Hemming asked on 10 Sep 2010, 10:36 PM

Requirements

RadControls version

Q1 2009+

.NET version

3.5

Visual Studio version

2008

programming language

C#

browser support

all browsers supported by RadControls


A generic method for Custom Sorting in RadGrid using LINQ

I imagine that if you've found yourself needing to apply custom sorting in a RadGrid, you started, like me, by looking at the XXXXXXX custom sort pages in the RadGrid documentation. Like me you may have wondered if you needed to code for each of the sort options in your grid.

I found myself needing to allow sorting in a grid that

  • got it's data from a WCF service, and
  • paged the data (which could, potentially, run to thousands of records)

The grid itself had a large number of columns and the requirements document said that the grid had to

  • allow multi-column sorting,
  • allow natural sorting, and
  • allow for the provision of a default sort column.

 
My first aim was to work out how I might code to sort on any number of columns in a multi-column table. A bit of googling turned up this stack overflow article which, in turn led me to this code snippet on http://aonnull.blogspot.com/. Adam's code provides an extension to the IEnumerable and IQueryable types that allows the developer to pass in a string representation of a search criterion and have it transformed in to Linq.

So, my next aim was to get RadGrid, when I opt to sort a grid, to provide me with a representation of the sort criteria that I could use in Adam's code.

This turned out to be a little less than trivial because whilst all of this info is available via certain RadGrid properties, it's not in one place.

The upshot was that I needed, and so created a set of helper methods (which could, I'm sure with just a little effort, be tranmsformed in to extension methods on the GridTableView object).

These helper methods are shown below.

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
using Telerik.Web.UI;
using System.Web.UI.WebControls;
  
public static class RadGridHelper
{
    public static string GenerateOrderByExpression(GridTableView RadGridTableToSort, GridSortCommandEventArgs SortCommandArgs)
    {
        if (RadGridTableToSort.AllowMultiColumnSorting)
        {
            return GenerateMultiColumnExpression(RadGridTableToSort, SortCommandArgs);
        }
        else
        {
            return GenerateSingleColumnExpression(RadGridTableToSort, SortCommandArgs);
        }
    }
  
    static string GenerateMultiColumnExpression(GridTableView RadGridTableToSort, GridSortCommandEventArgs SortCommandArgs)
    {
        List<GridSortExpression> sort = new List<GridSortExpression>();
        foreach (GridSortExpression x in RadGridTableToSort.SortExpressions)
        {
            sort.Add(new GridSortExpression{ FieldName = x.FieldName, SortOrder = x.SortOrder });
        }
  
        GridSortExpression sortExp = sort.FirstOrDefault(exp => exp.FieldName == SortCommandArgs.SortExpression);
        if (sortExp != null)
        {
            if (SortCommandArgs.NewSortOrder == GridSortOrder.None)
            {
                sort.Remove(sortExp);
            }
            else
            {
                sortExp.SortOrder = SortCommandArgs.NewSortOrder;
            }
        }
        else
        {
            if (SortCommandArgs.NewSortOrder != GridSortOrder.None)
            {
                sort.Add(new GridSortExpression{ FieldName = SortCommandArgs.SortExpression, SortOrder = SortCommandArgs.NewSortOrder });
            }
        }
        return GetSortExpressionString(sort);
    }
  
    static string GenerateSingleColumnExpression(GridTableView RadGridTableToSort, GridSortCommandEventArgs SortCommandArgs)
    {
        string defaultSortExpression = GetDefaultSortColumn(RadGridTableToSort);
  
        // if the sort order is null, return the default sort order.
        if (SortCommandArgs.NewSortOrder == GridSortOrder.None)
        {
            return String.IsNullOrEmpty(defaultSortExpression) ? String.Empty : GetSortExpressionString(new GridSortExpression { FieldName = defaultSortExpression, SortOrder = GridSortOrder.Ascending });
        }
  
        GridSortExpression newExplicitSortExpression = new GridSortExpression { FieldName = SortCommandArgs.SortExpression, SortOrder = GridSortOrder.Ascending };
        if (String.IsNullOrEmpty(defaultSortExpression))
        {
            return GetSortExpressionString(newExplicitSortExpression);
        }
        else
        {
            return GetSortExpressionString(new List<GridSortExpression>
            {
                newExplicitSortExpression,
                new GridSortExpression { FieldName = defaultSortExpression, SortOrder = GridSortOrder.Ascending }
            });
        }
    }
      
  
    static string GetSortExpressionString(GridSortExpression SortExpression)
    {
        if (SortExpression == null)
        {
            return String.Empty;
        }
        return String.Format("{0} {1}", SortExpression.FieldName, SortExpression.SortOrderAsString());
    }
  
    static string GetSortExpressionString(List<GridSortExpression> SortExpressions)
    {
        StringBuilder sb = new StringBuilder();
        bool first = true;
        SortExpressions.ForEach(exp =>
                                {
                                    if (first)
                                    {
                                        first = false;
                                    }
                                    else
                                    {
                                        sb.Append(", ");
                                    }
  
                                    sb.Append(GetSortExpressionString(exp));
                                });
        return sb.ToString();
    }
  
    static string GetDefaultSortColumn(GridTableView RadGridTableToSort)
    {
        string retValue = String.Empty;
        WebControl c = RadGridTableToSort as WebControl;
        if (c.HasAttributes && !String.IsNullOrEmpty(c.Attributes["DefaultColumnSortExpression"]))
        {
            retValue = c.Attributes["DefaultColumnSortExpression"];
        }
        return retValue;
    }
}

The GenerateOrderByExpression method, the only public method in the class, returns a string representation of the requested sorting of the grid. This method is required 'cos the SortCommand event arguments only give you the details of the column being sorted on now. This is OK if AllowMulticolumnSorting="false", but if it is true, there's nothning to tell you anything about the other columns being sorted on.

The GenerateOrderByExpression method calls one of 2 other methods depending on whether the RadGrid table is defined for multicolumn sorting or not.

However, before we look at these 2 methods, let's consider the concept of a default sort column. My requirements document said that a grid will display data in the order of a named column unless a different order is specifically set by the user and then, the sorted data will, where duplicates in the sorted column(s) exist, be sorted by the named default column. What this means is that regardless of what sort order the user selects, the data will /also/ be sorted by the named default column. In practice, this meant checking that the user wasn't sorting by the default column but in a different order and adding the default column (with a sort direction of ASC) to the end of any sort expression.

The methods GenerateMultiColumnExpression() and GenerateSingleColumnExpression() both create a string in the format "ColumnName ASC|DESC[, [...]]" which is passed via the WCF services to the Data Access Layer where the pre-existing Linq-to-Sql was modified to include a called to Adam's OrderBy() extension method.

A couple of things to note. I wrote the RadGrid helper methods to take a GridTableView as a parameter so that the code could be called for both the RadGrid's MasterTableView and any DetailTables. The Default Sort Order attribute is defined using the fact that the GridTableView is descended from a WebControl. You need to be sure that your code, specifically in your DAL takes into account that the supplied OrderBy parameter may be an empty string.

Finally, below, is a simple example of using the helper methods and Adan's extension method in a single tier app's SortCommand event handler.

protected void RadGrid1_SortCommand(object sender, GridSortCommandEventArgs e)
{
    string orderbyExpression = GenerateOrderByExpression(e.Item.OwnerTableView, e);
    e.Item.OwnerTableView.DataSource = data.OrderBy(orderbyExpression);
    e.Item.OwnerTableView.Rebind();
}

1 Answer, 1 is accepted

Sort by
0
Veli
Telerik team
answered on 13 Sep 2010, 12:16 PM
Hello Stuart,

Thank you for your time and effort. However, getting the sort expressions of a RadGrid GridTableView as an SQL-style OrderBy string can be simplified to a single method:

public static class RadGridHelper
{
    public static string GetOrderByExpression(GridSortCommandEventArgs e)
    {
        string orderBy = String.Empty;
        GridTableView sortedTable = e.Item.OwnerTableView;
 
        foreach (GridSortExpression expr in sortedTable.SortExpressions)
        {
            if (expr.FieldName != e.SortExpression)
            {
                orderBy += expr.FieldName + " " + expr.SortOrderAsString() + ",";
            }
        }
 
        if (e.NewSortOrder != GridSortOrder.None)
        {
            orderBy += e.SortExpression + " " + GridSortExpression.SortOrderAsString(e.NewSortOrder);
        }
        else if (orderBy.EndsWith(","))
        {
            orderBy = orderBy.Remove(orderBy.LastIndexOf(","), 1);
        }
 
        return orderBy;
    }
}

As I have tried to explain in the forum thread you have opened on the same topic, you need to take the currently present sort expressions from the GridTableView.SortExpressions collection, add the current sort expression from the command event arguments and build an OrderBy string. In this process, you should not modify the SortExpressions collection manually. It is managed by RadGrid.

As for the default sort column, adding a sort expression on initial load would be sufficient to have a grid column sorted by default:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        RadGrid1.MasterTableView.SortExpressions.Add(new GridSortExpression { FieldName = "ID", SortOrder = GridSortOrder.Descending });
    }
}

This sort expression will be included when building the OrderBy string.

We believe the implementation details for this topic are straightforward enough to not require a separate Code Library entry for this particular scenario. Also, the forum thread you have already opened on the topic leaves ground for discussion by the community. Still, we thank you for the effort of putting this example together and providing a solution for this frequent scenario.

Regards,
Veli
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
Tags
Grid
Asked by
Stuart Hemming
Top achievements
Rank 2
Answers by
Veli
Telerik team
Share this question
or