PropertyDescriptorDataAccessor

Grids for WPF Forum

Posted 9 years ago by Marcel Konnegen
Avatar
Hello Support,
we have a custom TypeDescriptorFactory in which we have overridden the GetProperties(object value, ...)method. In addition, we have a class in which we have properties that are decorated with custom attributes. What we now need is to implement a custom PropertyDataAccessor. We have created such a custom PropertyDataAccessor and inherited from PropertyDescriptorDataAccor. We wrap any PropertyDataAccessor from the base.GetProperties call and delegate each method call to the wrapped (ActiPro)-PropertyDataAccessor. Following is a short illustration:

public class Custom : PropertyDescriptorDataAccessorBase
{
    public Custom(PropertyDescriptorDataAccessorBase wrap)
    {
       _custom = wrap;
    }

    public override IPropertyDataAccessor Parent
    {
        get { return _custom.Parent; }
    }

    ...
}
Now we have the problem, that the property grid is not updating, when we change the value of a property from code (some binding-problem possibly???). If we leave out the intermediate layer of our custom PropertyDataAccessor, then the Bindings work fine and the PropertyGrid updates correctly. How do we correctly implement a wrapper for an PropertyDataAccessorBase???


With regards
Marcel

Comments (7)

Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Marcel,

You would need to ensure to delegate/forward the event handlers as well, such as the INotifyPropertyChanged.PropertyChanged event.

So effectively, you'd have to implement IDataAccessorNotifier and INotifyPropertyChanged and forward the events from the wrapped object (so the UI can update appropriately). These interfaces are already implemented by DataAccessorBase, so you'd just have to call OnPropertyChanged (for INotifyPropertyChanged) or OnNotify (for IDataAccessorNotifier) appropriately.


Actipro Software Support

Posted 9 years ago by Marcel Konnegen
Avatar
Hello Support,
thank you for the hint. We now inherit from PropertyDescriptorDataAccessor and pass the PropertyDescriptorDataAccessor, that is to be wrapped, to the base class as the parent.

Now the UI updates work as expected. But now another problem has occured. We have multiple properties which are annotated with ExpandableCollectionTypeConverters. However our overridden CanAddItem() method is not invoked anymore. Is there something we need to implement inside our custom PropertyDescriptorDataAccessor?

Update: We have identified the problem to be the following. Somehow, the PropertyDescriptorDataAccessor returns the wrong value for CanAddChild, AddChild(). Following is an example to illustrate the problem:

public OSPropertyDataAccessor(PropertyDescriptorDataAccessorBase dataAccessor)
   : base(dataAccessor, dataAccessor.Target, dataAccessor.PropertyDescriptor)
{
   bool canAdd = dataAccessor.CanAddChild; //is true
   bool canAdd2 = base.CanAddChild; //is false

   dataAccessor.AddChild(); //Invokes correct method in TypeConverter
   base.AddChild(); //does nothing
}
Is there a reason why the constructor needs the target object, and the PropertyDescriptor in addition to the PropertyDescriptorDataAccessor??? Shouldn't these two properties be also extracted from the PropertyDescriptorDataAccessor that is passed to the base class???

Thanks for your help!
Marcel

[Modified at 12/17/2010 07:13 AM]

[Modified at 12/17/2010 08:06 AM]

[Modified at 12/17/2010 08:06 AM]
Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Marcel,

The IPropertyDataAccessor you pass to the PropertyDescriptorDataAccessor should be a parent property. There are two types of properties displayed in the PropertyGrid by default. The first are pulled directly from the SelectedObject(s) (or any static properties). Some of those properties may be themselves expandable, and thus have child properties. In this latter case, the child property has a "parent" property (which is passed in that constructor). In the former case, there is no parent property and null should be passed to that constructor.

In your case, you are making the wrapped IPropertyDataAccessor the parent of your custom property, which is incorrect. You shouldn't need to pass a parent IPropertyDataAccessor at all, since the relationships will already be hooked up in the IPropertyDataAccessor instances you are wrapping.


Actipro Software Support

Posted 9 years ago by Marcel Konnegen
Avatar
Hello Support,
what we intended by this was to save as much code as possible. We could always just implement the appropriate interfaces and implement them ourselves. But we thought, that there is an appropriate base class implementation which we could pass our PropertyDescriptorAccessor and only implement out our additional properties that we need. Is there an appropriate wrapper class, or would we need to implement the appropriate interfaces and delegate 95% of the calls to the PropertyDescriptorDataAccessor that is to be wrapped?

Do you by any chance have any example code or how-to's available on custom implementations of PropertyDataAccessors???

Thank you for your help!

With regards,
Marcel

[Modified at 12/17/2010 02:32 PM]
Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Marcel,

Sorry, but we don't currently have a sample like that. The main issue here is that there are actually 4 classes that implement IPropertyDataAccessor. The most common is PropertyDescriptorDataAccessor, but there is also ImmutablePropertyDescriptorDataAccessor (for properties on immutable objects), CollectionPropertyDescriptorDataAccessor (for collection entries when collections are expandable), and MergedPropertyDataAccessor (when displaying the properties of more than 1 object).

The GetProperties(object, DataFactoryOptions) method can return any of the above, except MergedPropertyDataAccessor. Depending on what settings you are using, what properties you are displaying, and what you are trying accomplish, you may need to "wrap" each of these types.

Assuming you only need to override instances of PropertyDescriptorDataAccessor, you can:

1. Create a class that derives from PropertyDescriptorDataAccessor.
2. Add a constructor like so:
public MyPropertyDescriptorDataAccessor(IPropertyDataAccessor parent, object target, PropertyDescriptor propertyDescriptor)
    : base(parent, target, propertyDescriptor) {
    // No-op
}
3. Create a class that derives from TypeDescriptorDataFactory (or TypeReflectionDataFactory).
4. Override the GetProperties(object, DataFactoryOptions) method and implement like so:
protected override IList<IPropertyDataAccessor> GetProperties(object value, DataFactoryOptions options) {
    IList<IPropertyDataAccessor> properties = base.GetProperties(value, options);
    if (properties != null) {
        for (int i = 0; i < properties.Count; i++) {
            PropertyDescriptorDataAccessor property = properties[i] as PropertyDescriptorDataAccessor;
            if (property != null)
                properties[i] = new MyPropertyDescriptorDataAccessor(property.Parent, property.Target, property.PropertyDescriptor);
        }
    }
    return properties;
}
5. Assign PropertyGrid.DataFactory to an instance of your custom DataFactory.

This effectively replaces the instances of PropertyDescriptorDataAccessor with an instance of your MyPropertyDescriptorDataAccessor that references the same property on the same object. In addition, the same concept could be applied to ImmutablePropertyDescriptorDataAccessor and/or CollectionPropertyDescriptorDataAccessor.

Then in your custom PropertyDescriptorDataAccessor you can override methods or properties as needed.

For the upcoming release, we've added some additional virtual methods to DataFactory to make this easier. With the new virtual methods, step 4 above can be condensed to:
protected override IPropertyDataAccessor CreatePropertyDataAccessor(IPropertyDataAccessor parent, object value, PropertyDescriptor propertyDescriptor) {
    return new MyPropertyDescriptorDataAccessor(parent, value, propertyDescriptor);
}
Hope this helps.


Actipro Software Support

Posted 9 years ago by Marcel Konnegen
Avatar
Hello Support,
that has solved our problem. We were wrapping CollectionPropertyDescriptorDataAccessor with a PropertyDescriptorDataAccessor, which made its CanAddChild-Property return the incorrect value. We now distinguish between Collection and plain object data accessors which made it work smoothly.

As a little side mark: A decent documentation on your API might have helped us a lot. (-:

Thank you anyways for the great and quick support!!!
Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Marcel,

Glad you got it worked out. We've updated our topic on building custom factories a bit to include information we discussed, but was missing from the topic.


Actipro Software Support

The latest build of this product (v2019.1 build 0681) was released 2 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.