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