0

I have made a custom Searchable ComboBox, with a Filter defined on the default CollectionView

CollectionViewSource.GetDefaultView(this.ItemsSource)
view.Filter += FilterPredicate;

The filter predicate of the ComboBox :

private bool FilterPredicate(object value)
{
    return PropertyPathHelper.GetValue(value, FilterPropertyPath).ToString().Contains(PART_FilterTextBox.Text);
}
//value is instance of current filtered item: Student{ FullName="SunnyXyz" , Age=30 }
//& FilterPropertyPath="FullName"

FilterPropertyPath is a "string" DependancyProperty, that acts similar to DisplayMemberPath, to locate the text property on which to apply the filtering in the bound item. PropertyPathHelper.GetValue creates a dummy binding & resolves the bind path, but this method is slow/inelegant/doesn't seem like the right approach. (from https://stackoverflow.com/a/7041604/852318)

Can anyone help with an alternative right way or a more elegant way to pass the FilterPropertyPath information

Community
  • 1
  • 1
Sunny
  • 119
  • 1
  • 2
  • 13
  • I'm not sure I fully understand the question, but you can use reflection to get the property value dynamically from a string. `value.GetType().GetProperty(FilterPropertyPath).GetValue(value)` Note that GetProperty returns null if the property with the given name is not found. – Szabolcs Dézsi Apr 06 '15 at 14:39
  • @SzabolcsDézsi, Two concerns regarding this approach, 1. reflection is terribly slow, 2. I need this to work with nested path? like Class A { Class B { Class C { string GetMe { get; set; } } } }, then value.GetType().GetProperty("A.B.C.GetMe").GetValue(value) – Sunny Apr 07 '15 at 06:55
  • You sir are right. Haven't thought about nested paths. – Szabolcs Dézsi Apr 07 '15 at 06:57

1 Answers1

1

You can use Expression Trees in order to achive your target.

You can use an helper class for building and then executing an expression which gives you the value of your PropertyPath. Here the code:

public static class PropertyPathHelper
{
    public static T GetValue<T>(object instance, string propPath)
    {
        Delegate runtimeDelegate;

        System.Linq.Expressions.ParameterExpression instanceParameter =
            System.Linq.Expressions.Expression.Parameter(instance.GetType(), "p");

        string[] properties = propPath.Split('.');

        System.Linq.Expressions.MemberExpression currentExpression =
            System.Linq.Expressions.Expression.PropertyOrField(instanceParameter, properties[0]);

        System.Linq.Expressions.LambdaExpression lambdaExpression =
            System.Linq.Expressions.Expression.Lambda(currentExpression, instanceParameter);

        for (int i = 1; i < properties.Length; i++)
        {
            currentExpression = System.Linq.Expressions.Expression.PropertyOrField(lambdaExpression.Body, properties[i]);
            lambdaExpression = System.Linq.Expressions.Expression.Lambda(currentExpression, instanceParameter);
        }

        runtimeDelegate = lambdaExpression.Compile();

        return (T)runtimeDelegate.DynamicInvoke(instance);
    }
}

Of course you can improve that method by using a static dictionary which stores the compiled lambda expressions. Expressions are faster than using reflection. You can use this method in this way:

One one = new One() { Two = new Two() { Three = new Three() { Description = "StackOverflow" } } };
string result = PropertyPathHelper.GetValue<string>(one, "Two.Three.Description");

The "result" string will be set to "StackOverflow". Therefore your filter predicate will become:

private bool FilterPredicate(object value)
{
    return PropertyPathHelper.GetValue<string>(value, FilterPropertyPath).Contains(PART_FilterTextBox.Text);
}

I hope it can help you.

Il Vic
  • 5,576
  • 4
  • 26
  • 37
  • Using lambda expressions instead of reflections seems like a really smart move ( will try to benchmark & update speed comparison ) – Sunny Apr 07 '15 at 06:51
  • @II Vic, the code was excellent, didn't have to run any synthetic benchmark, had a dummy application where a specific user control's binding expressions were being resolved by creating a new dummy binding; Replacing that code with your Expression Trees approach improved performance to a significant noticeable extend (tried several times, where the only change was this method). – Sunny Apr 13 '15 at 06:24