Hi all, I’m back, and apologize for the delay, things have been crazy here and I’ve taken a little time off following shipping VS2010, so I’m getting back into it. I’d like to talk about one of my favorite late additions to the WF4 designer programming model. We added this change sometime in the RC/RTM timeframe in response to some feedback that we had gotten about the verbose-ness of programming against ModelItem.
If you recall from this post, ModelItem exists to serve as a INotifyPropertyChanged (INPC) aware proxy to the underlying instance being edited within the designer. In exchange for this fairly flexible proxy layer, you get in return a pretty verbose way in which one has to interact to actually get to the data. Often times there are fairly long incantations of myModelItem.Properties[“foo”].Value.Properties[“bar”].Value.GetCurrentValue() in order to actually return the data (or be able to set the value). From the beginning, we had decided we wanted to optimize for the WPF XAML consumer of the model item, and so we chose to implement ICustomTypeDescriptor so that WPF binding would be pretty simple (the above would result in a binding syntax of ModelItem.foo.bar).
One quick note, looking at the definition of ModelItem itself will show that only the INPC interface is implemented. However, ModelItem is an abstract type, and the implementation subclass ModelItemImpl contains the definition and implementation of the following interfaces ModelItem, IModelTreeItem, ICustomTypeDescriptor, IDynamicMetaObjectProvider
As we worked with some of our customers, it became clear that they were frequently using ModelItem in code as well, and so the programming model was becoming more of a problem (we had somewhat hoped that the majority of customers would be using XAML). We initially went with the idea that we would create a series of Design time peers to all of the common types in WF (making a SequenceDesign, ParallelDesign, etc) which provided an identical programming surface to its peer, but under the covers in the property getters and setters would redirect to an underlying ModelItem. This has the advantage that we could create the appearance of a strongly typed API surface, but has the downside that it introduces an extra type for every activity we ship, and it requires customers to create two types as well, and not just for activities, you would want one for customer too. The other approach we kicked around was using the Dynamic capabilities introduced in .NET 4.
It’s important to note that these are ultimately complimentary approaches, that is, it’s possible in a future framework release (or in a codeplex project if anyone is looking to for a little fun on the side) you could release the strongly typed design time peers. From a timing perspective, it was not likely that we could introduce 30+ new types in the RC/RTM release, and we had questions about the long term sustainability of requiring an additional type for activities. So, we went ahead with the plan for dynamic support. In order to do this, we had to add an implementation of IDynamicMetaObjectProvider. Bill Wagner has a good article here on the basics of doing this. This requires us to implement one method, GetMetaObject. In our case, we have an internal, nested ModelItemMetaObject type which inherits from System.Dynamic.DynamicMetaObject.
In our case, there are basically two important methods that we care about, BindGetMember and BindSetMember. These wire up to two methods on ModelItemImpl to get and set based on the property name. The net result of this is that the following code
root.Properties["Residence"]. Value. Properties["StreetAddress"]. Value.GetCurrentValue()
Can now be written as
dynamic mi = root; Console.WriteLine(root.Residence.StreetAddress)
Now, with anything there are some tradeoffs, the biggest one is that this is dynamic, I don’t get compile time checking, and I could have a type that will result in an exception being thrown (that said, that problem exists in the verbose form as well). This does let me write much more succinct code when programming the model item tree though for both gets and sets. As an added bonus, we found there is actually a decent perf improvement from the WPF XAML data binding that now leverages the IDynamicMetaObjectProvider (mentioned in ScottGu’s post here) in the way that the runtime will only compute the way to resolve the property once (as opposed to every time when it goes through the ICustomTypeDescriptor code path).