[Update 08/03/11: fixed a few horrible proofreading issues which slipped past my nonexistant editor]
Today we’re going to explore some internal workings of ModelItem - or really its concrete subclass ModelItemImpl. ModelItemImpl is an internal class, making all of what we talk about below ‘undocumented implementation detail’, (according to some narrow view based on documenting only APIs which are marked ‘public’), and therefore potentially invalid in future framework versions. Now stop worrying about such minor details and read on, because this stuff is interesting!
When you getting started with writing custom activity designers, and even if you have done WPF before, you'll notice it's kind of mysterious that this word 'ModelItem' shows up pervasively all the activity designer examples.
<TextBox Content="{Binding ModelItem.OwnerName}”/>
What’s the point of all this?
Using ModelItem gives you an important feature for making WPF binding work, which is change notification from a wrapped object. Instead of binding straight to a literal property on your activity, instead you bind to a proxy of the property on a proxy object. In other words, a ModelProperty on a ModelItem. ModelItem does the work of implementing INotifyPropertyChanged while your runnable activity class itself does not implement this interface.
Fine in principle. Now dig down a little deeper. What really happens inside WPF when you bind to ModelItem.OwnerName? How does this text 'ModelItem.Owner' actually turn into a change notification listener, considering that a ModelItem object does not literally have any public property called OwnerName?
Here’s the basic things happening.
[Note: Possibly WPF might also support dynamic (C# 4.0) property access to an IDynamicMetadataObjectProvider? I haven't checked this yet.]
Now presumably doing a binding on ModelItem has to work by one of these standard mechanisms that WPF uses. If ModelItem plays really nice we can expect it might allow us to use the same full set of mechanisms for representing the property on the object it wraps, and consistently surface them through to the PropertyPath targetting it just as if we were using a PropertyPath on the underlying object – just with the addition of change notification.
If you grab a ModelItem instance from your custom ActivityDesigner, I think you’ll consistently find that what you’ve actually got is a ModelItemImpl. And if you check out its interfaces, you can find that even though it is of an internal type, it has two public interfaces on it, ICustomTypeDescriptor and IDynamicMetadataObjectProvider, both of which, interestingly, appeared above.
Practically, which interfaces should it use to provide the properties to PropertyPath? For reasons of precedence, the best answer is to implement ICustomTypeDescriptor and in particular, ICustomTypeDescriptor.GetProperties().
If ModelItemImpl is going to play nice, and allow your WPF bindings to work consistently as they would have with the original objet, then it should try to respect the behavior of the object that it wraps, including where that object itself implements ICustomTypeDescriptor.
So let’s try it out by implementing ICustomTypeDescriptor.
public class MyCustomTypeDescriptorActivity : CodeActivity, ICustomTypeDescriptor
{ …
What’s a reasonable default behavior for the ICustomTypeDescriptor methods? Well, bear in mind that I’m just trying this out for the first time, but I think the reasonable default implementation for ICustomTypeDescriptor methods could look like this:
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetProvider(this) .GetTypeDescriptor(this) .GetAttributes();
}
In order for us to ‘add’ a property to our object via Custom Type Descriptor, we will need to deviate from the above template in GetProperties(). (And possibly, GetProperties(Attribute[] attributes).)
public PropertyDescriptorCollection GetProperties()
// Get default property descriptors
PropertyDescriptorCollection r = TypeDescriptor.GetProvider(this)
.GetTypeDescriptor(this)
.GetProperties();
// Copy them to an array
int n = r.Count;
PropertyDescriptor[] arr = new PropertyDescriptor[n + 1];
r.CopyTo(arr, 0);
// Add a new custom property descriptor to array
SimplePropertyDescriptor pd = new SimplePropertyDescriptor();
arr[n] = pd;
// Return a new PropertyDescriptorCollection
var ret = new PropertyDescriptorCollection(arr);
return ret;
Lastly, we need to implement our own PropertyDescriptor type, SimplePropertyDescriptor.
public class SimplePropertyDescriptor : PropertyDescriptor
public SimplePropertyDescriptor()
: base("SimplePropertyDescriptor", new Attribute[0])
public override bool CanResetValue(object component)
return false;
public override Type ComponentType
get { return typeof(MyCustomTypeDescriptorActivity); }
public override bool IsBrowsable
get { return true; }
public override Type PropertyType
get { return typeof(string); }
public override object GetValue(object component)
return "Hello";
public override bool IsReadOnly
get { return false; }
public override void ResetValue(object component)
public override void SetValue(object component, object value)
throw new NotImplementedException();
public override bool SupportsChangeEvents
get
public override bool ShouldSerializeValue(object component)
Well, I’m happy to report that yes, ModelItem detects and interoperates with our property descriptor. We can see it in the property grid. And also use it with a ModelItem Binding expression.
Notes:
If you want your property to show up in the Workflow Designer’s property grid, there’s a couple things you need to do exactly right:
You can workaround the issue of IsBrowsable = false being ignored by adding a BrowsableAttribute(false) on your custom property descriptor (in the attribute array, in the call to the base constructor).