Donnerstag, 12. April 2012

Retrieving the value of a property from a DataBound object

In WPF it's easy to bind a object to a DependencyProperty and can be accessed just like any normal Property. You can even access the Properties of that object and get the values of these if you know what type they are.
But what is if the Binding is of a random type and you would like to access the value of a property on that unknown?

Let's say for example you want to add a Filtering to the ComboBox.
ComboBoxes can bind to any type as long as it implements IEnumerable. But for Filtering you need to know the value of a Property inside the objects in the IEnumerable.

To access the value of a Property on the Bound objects, you need to create a new Binding on the desired Property for each of the objects in the IEnumerable. The thing you need to tell the ComboBox is the name of the Property of which you would like to get the value of. In the example I simply created a DependencyProperty containing the name(path). You could also use the DisplayMemberPath or analyse the Template of the Bound objects.

public bool Filter(object obj)
{
    // get the value from the property out of the bound object
    var value = GetValueFromBindingPath(obj, FilterMemberPath);
    if(value != null)
    {
        if(value.ToString().Contains(FilterString))
           return true;
    }
    return false;
}

/// <summary>
/// the name of the property that the filtering should be made on in the bound objects
/// </summary>
public string FilterMemberPath
{
    get { return (string)GetValue(FilterMemberPathProperty); }
    set { SetValue(FilterMemberPathProperty, value); }
}

public static readonly DependencyProperty FilterMemberPathProperty =
    DependencyProperty.Register("FilterMemberPath", typeof(string), typeof(ComboBoxExt), 
                                new UIPropertyMetadata(null));

#region Value from Binding / Path

// 
// we create a virtual binding and bind to the property from the
// path that was set in the FiltermemberPath property
// with the binding we can easily retreave the object that is
// contained behind the property
//
// tests with over 5000 items have shown 
// that this has no impact on performance
//

/// <summary>
/// gets the object/value from a property from the object according to the path
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyPath"></param>
/// <returns></returns>
public static object GetValueFromBindingPath(object obj, string propertyPath)
{
    Binding binding = new Binding(propertyPath);
    binding.Mode = BindingMode.OneTime;
    binding.Source = obj;
    BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
    return _dummy.GetValue(Dummy.ValueProperty);
}

// dummy object for getting the value from a path on a object
private static readonly Dummy _dummy = new Dummy();

private class Dummy : DependencyObject
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(Dummy), 
                                    new UIPropertyMetadata(null));
}

#endregion


This looks like a hack and maybe (most probably) it is a big one.
But it works quite well and its quite fast.

Keine Kommentare: