|
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(); }