Archive

Archive for September, 2015

Custom Sorting with Kendo Grid in ASP.NET MVC

September 16, 2015 Leave a comment

So we ran into a problem at work when using Telerik’s Kendo MVC UI package. It turns out that there is no way to server-sort on columns if the column names don’t match the actual data model property names.

In our case, the names didn’t match because we’re using ViewModels for the grid, and this particular viewmodel wrapped two properties in one.

It turns out that the DataSourceRequest object that Kendo sends to the controller has a Sorts collection, and this collection can be modified. So it was simply a matter of modifying and adding to that collection, and the ToDataSourceResult() method performed the sorts as intended.

But how to make this generic so we could apply it to any view model? Attributes to the rescue!

public class SortedAttribute : Attribute {
    public SortedAttribute(params string[] dataPropertyNames) {
        DataPropertyNames = dataPropertyNames;
    }
    public IEnumerable<string> DataPropertyNames { get; set; }
}

Here’s a viewmodel using our new attribute.

    public class MyViewModel {
        int Id { get; set; }
        
        string Description { get; set; }
        
        [Sorted("Customer.Id", "Customer.Name")]
        string Customer { get; set; }
        
        [Sorted("Location.Id", "Location.Description")]
        string Location { get; set; }
    }

Then we just need to modify our request’s Sorts collection using the values in the attribute. I made this an extension method to make it easy to call from the controller method.

public static void UpdateSort<T>(this DataSourceRequest request) {
    //get properties that have a Filtered attribute
    var propertiesToChange = typeof(T).GetProperties()
        .Where(p => request.Sorts.Select(s => s.Member).Contains(p.Name) && p.GetCustomAttributes(typeof(SortedAttribute), false).Length > 0);

    foreach (var sortProperty in propertiesToChange) {
        //get filtered attribute (indexing is okay because we're only looping through ones we know have one)
        var sortedAttribute = (SortedAttribute)sortProperty.GetCustomAttributes(typeof(SortedAttribute), false)[0];

        //get the current Kendo sort descriptor
        var currentSort = request.Sorts.Single(s => s.Member == sortProperty.Name);
        //update the name to be the first data property name
        currentSort.Member = sortedAttribute.DataPropertyNames.FirstOrDefault() ?? sortProperty.Name;

        //if there are other data property names, add them to the sort, using the same sort direction
        foreach (var dataPropertyName in sortedAttribute.DataPropertyNames.Skip(1)) {
            request.Sorts.Insert(request.Sorts.IndexOf(currentSort) + 1, new SortDescriptor(dataPropertyName, currentSort.SortDirection));
        }
    }
}

Now we can just call it like this:

public ActionResult Get([DataSourceRequest] DataSourceRequest request) {
    var data = Context.MyData;

    request.UpdateSort<MyViewModel>();

    var results = data.ToDataSourceResult(request, ModelState, e => new MyViewModel {
        Id = e.Id,
        Description = e.Description,
        Customer = String.Format("{0} - {1}", e.Customer.Id, e.Customer.Name),
        Location = String.Format("{0:000} - {1}", e.Location.Id, e.Location.Description)
    });

    return Json(results, JsonRequestBehavior.AllowGet);
}

Why Telerik hasn’t built similar functionality into their product is beyond me. Microsoft had custom sort properties in their WebForms grid 10 years ago. Anyway, I hope this helps someone!