Posts mit dem Label Property werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Property werden angezeigt. Alle Posts anzeigen

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.

Dienstag, 10. Januar 2012

Set Properties to ReadOnly at runtime

Lately I needed to set the ReadOnly Attribute of a Property in C# at runtime. I wanted to change the IsReadOnly Property dynamically. The problem was that the IsReadOnly Property of the ReadOnlyAttribute does not contain a setter meaning that it is ReadOnly...

After doing a lot of searching I stumbled on this Post by David Morton. Here he describes how to use Reflection to edit internal fields.

I created a Method that uses Reflection to change the IsReadOnly Property of the ReadOnlyAttribute.
     /// <summary>  
     /// Uses reflection to set the readonly attribute on a property.  
     /// This value is set to the class definition and not to the instance.   
     /// If it is set once than it has to be reset again to get the default.  
     /// </summary>  
     /// <param name="instance">instance containing the property</param>  
     /// <param name="property">the property to set the readonly value</param>  
     /// <param name="value">value to set to the property</param>  
     private static void SetReadOnly(object instance, string property, bool value)  
     {  
       // Create a PropertyDescriptor for the Property by calling the static 
       // GetProperties on TypeDescriptor.  
       PropertyDescriptor descriptor = 
                        TypeDescriptor.GetProperties(instance.GetType())[property];  
       // Fetch the ReadOnlyAttribute from the descriptor.   
       ReadOnlyAttribute attrib = 
                       (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];  
       // Get the internal isReadOnly field from the ReadOnlyAttribute using reflection.   
       System.Reflection.FieldInfo isReadOnly = 
                       attrib.GetType().GetField("isReadOnly", 
                              System.Reflection.BindingFlags.NonPublic | 
                              System.Reflection.BindingFlags.Instance);  
       // Using Reflection, set the internal isReadOnly field.   
       isReadOnly.SetValue(attrib, value);   
     }  

In this excample I want to change the value of the ReadOnlyAttribute of the TestProperty.
   class PropertyTest  
   {  
     string _testProperty;  
     [ReadOnly(false)]  
     public string TestProperty  
     {  
       get  
       {  
         return _testProperty;  
       }  
       set  
       {  
         _testProperty = value;  
       }  
     }  
   }  
Now I can pass a instance and make any Property I want ReadOnly or Readable again.
     public void Edit()  
     {  
       var instance = new PropertyTest();  
       SetReadOnly(instance, "TestProperty", true);  
     }  
A big drawback about this is that it breaks encapsulation.
Another problem with this approach ist that it changes the Attribute in the Class definition and not only in the instance. If a new object is created the ReadOnlyAttribute of Property has the value that was set previously. In that case the Attribute has to be set every time a new Instance is created.