Filtering Collection Properties with RadGridView for Silverlight and WPF

Monday, December 05, 2011 by Rossen Hristov | Comments 4

By default, RadGridView will not be able to filter properties which are IEnumerable. However, with two of its features you can easily add this custom functionality. The first feature you need to use are the Custom Filtering Controls which I have explained in one of my earlier blog posts so I will not go into detail here.

The focus of this blog is how to implement the IFilterDescriptor interface so that our data engine can filter your enumerable property. This interface allows you to provide a filtering expression. This expression will be used when the data engine performs the actual filtering. In the sample project that I have attached I have bound the grid to a list of football players. Each player has an IEnumerable<string> property called FormerClubs.

So, if you were to filter the players collection outside RadGridView by using LINQ you would write something like this:

this.players.Where(player => player.FormerClubs.Contains(”Liverpool”));

When you implement the IFilterDescriptor interface you have to return the player.FormerClubs.Contains(”Liverpool”) part from the CreateFilterExpression method. Our data engine will take the expression that you provide, place it in a Where clause and take care of all the rest. Here is my implementation of the IFilterDescriptor interface:

private static readonly MethodInfo EnumerableCastMethod = typeof(Enumerable).GetMethod("Cast");
private static MethodInfo GenericContainsMethod = GetGenericContainsMethodInfo();

private static MethodInfo GetGenericContainsMethodInfo()
{
    // get the Enumerable.Contains<TSource>(IEnumerable<TSource> source, TSource value) method,
    // because it is impossible to get it through Type.GetMethod().
    var methodCall = ((MethodCallExpression)((Expression<Func<IEnumerable<object>, bool>>)(source => source.Contains(null))).Body).Method.GetGenericMethodDefinition();
    return methodCall.MakeGenericMethod(typeof(object));
}

/// <summary>
/// The whole 'magic' happens here. You need a basic knowledge about Linq Expressions
/// in order to be able to follow what is going on here.
/// The result of this method will be transformed into a Lambda and then put inside
/// a Where method call on the source collection.
/// </summary>
public Expression CreateFilterExpression(System.Linq.Expressions.Expression instance)
{
    // FormerClubs
    var propertyName = this.column.DataMemberBinding.Path.Path;

    // player.FormerClubs
    var collectionPropertyAccessor = Expression.Property(instance, propertyName);

    // player.FormerClubs.Cast<object>()
    var genericCollectionPropertyAccessor = Expression.Call(null
        , EnumerableCastMethod.MakeGenericMethod(new[] { typeof(object) })
        , collectionPropertyAccessor);

    // player.FormerClubs.Cast<object>().Contains("the value")
    Expression result = Expression.Call(GenericContainsMethod
        , genericCollectionPropertyAccessor
        , Expression.Constant(this.Value));

    if (this.FilterOperator == FilterOperator.DoesNotContain)
    {
        // !player.FormerClubs.Cast<object>().Contains("the value")
        result = Expression.Not(result);
    }

    return result;
}

You need to have basic understanding of LINQ Expressions in order to follow what I am doing here. I admit that getting the generic Contains method is a bit ugly, but I could not get it with simple reflection.

Here is what I got in the end. Since I am lacking uber-designer skills, I am sure that you can design the UI better than me. The expression stuff was the focus of this blog anyway.

FormerClubs

The fact that the Custom Filtering Controls feature allows you to create any user interface and the IFilterDescriptor interface allows you to create any filtering expression makes the combination of the two extremely powerful. You only need a guy who can draw nice stuff and a guy who can tackle those cryptic LINQ expressions.

Please, find the sample projects attached. There is a Silverlight and WPF version in the attached file.

Happy coding!

4 Comments

  • paulovila 06 Dec 2011
    Is it possible to filter aggregate over a collection navigation property?, for example: Bills and BillLines, filter bills where there are more than 3 BillLines.
    (It is not valid answer something like 'make a DTO')
  • Rossen Hristov 07 Dec 2011
    Of course it is possible. When you can build the respective LINQ Expression and the respective filtering UI everything is possible.
  • Devinder Gupta 20 Dec 2011

    Thank You
    The given Information on your blog is very useful.
     
    Visit :-<a href="http://www.dgsrealtors.com/">Dgs properties in  Delhi NCR</a>
  • Vasyl 16 May 2012
    How we can handle it if we have cllection of objects (not strings).
    E.g.collection of objects of type FootballClub with property "Name"?

Add comment

  1. Formatting options
       
     
     
     
     
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)