DM-V-VM part 6: Revisiting the data model

Sorry it’s taking me so long to get the posts out. The series turned out to be a little longer than I anticipated 🙂 I got a lot of good feedback on the Data Model stuff.

First off, I want to mention layering. The DataModel typically is a layer on top of some other lower level data model that’s not optimized to WPF. This might be something specific to the database technology you are using. Or, it could wrap some native object that’s accessed via interop (I’ve run into a couple of examples of people doing this recently).

Also, I made some simplifications in the first post that made them much less interesting. The big simplification was that the models only fetched their data once and weren’t live. Things get a lot more interesting when the models keep their data up to date.

Once you make them live, you run into a life time issue. If you have a large set of items, you only want to keep the visible items live. We’ll do this by giving models Activate and Deactivate functions that control when it is live. Let’s start with the DataModel changes. I’m not going to list the full class here, just the modifications. I’ll post the entire sample soon when I finish up the series. If you have any question about how to apply these changes, let me know.

First, an IsActive property, which is implemented much like the State property. We could make it settable to activate and deactivate the model, but I like to think of those as methods rather than a property change:

Note: We could have used a System.Threading.Timer to do the updates where we’d be called on a background thread directly, but then we couldn’t set the model state to fetching. We’d have to send that back to the UI thread.

Ok, now we’ve made it so you can activate it and deactivate it, but when do we do so? Let’s say we’ve got thousands of our models in a ListBox. It’s only going to only show a few of them on the screen at a time and we only want the ones on screen to be active. We’ll use the attached property trick to do this without having to write custom code each time we want to activate and deactivate models. The basic idea is that we’re going to display our model in a DataTemplate and we want to activate the model when FrameworkElements in the UI are loaded, and deactivate the model when they are unloaded. With the attached property trick, our DataTemplate Xaml just has to be:

We’ve registered a PropertyChangedCallback on the property, so any time it is changed (including when it’s initially set), OnModelInvalidated is going to be called. In OnModelInvalidated, we’re going to register for Loaded and Unloaded events on the the FrameworkElement we are attached to. We also have to do a bit of bookkeeping to clean up if we were previously pointing to a different model.

Pretty neat trick, isn’t it? This means it’s really easy for us to activate models when they are visible in the UI by simply adding the ActivateModel.Model property to the UI element. Since we know all FrameworkElements will get unloaded when they go away, we know we won’t have to worry about leaking anything. It doesn’t require any custom activate/deactivate code per view!

We get a form of data virtualization out of this trick. If the data is very expensive, the models can act as a relatively cheap shell. When you go to view a a large collection of items, you just need to provide a collection of the data models instead of the full items. The expensive data can then be accessed only when the UI for the data is visible on the screen and then thrown out when the data goes offscreen.

I’ll confess, I’ve made another simplification from what we’ve done in Max. The lifetime management of a lot of our models is slightly more complex. And, as much as we hated to do so, we ended up adding a reference counting to our models. So we keep track of multiple levels of activation and a model is live as long as that count is greater than zero. To get something a bit like smart pointers, we have Activate return an IDisposable which we call an “activation cookie”. Disposing of the activation cookie decrements the activation count. The cookie is the only way to decrement the count, there’s no public method on the model. It’s smart enough to not let you decrement multiple times. And, in debug builds, we have a finalizer on the cookie that asserts if Dispose wasn’t called, to help catch leaks. We were quite happy to leaving reference counting when moving to managed code, so it hurt a bit to bring it back 🙂

I’m going to try implementing this pattren for 3DObjects(MV3D). I know 3DObjects are not FrameWorkElements or UIElements, but I think not all but some of concepts of this pattern could be very usefull to implement binding between a 3DObject and a DataModel (to get the position in 3D space for example could be a possible Data from DataModel). Thanks a lot for great work! Waiting for next/last couples of posts…

There are a few things that steer me away from asynchronous data binding. The first is that the property getters will now be callable by multiple threads, and that makes the threading model for the object much more complicated. Second, I think it would be surprising that some properties could hang indefinitely, but others wouldn’t. As a consumer of the class, how would you know what’s safe to call? And, finally, you get more control if you do it yourself. With asynchronous binding, WPF creates the thread for you, but with this mechanism you have more control over scheduling updates and such.

In reviewing all of the parts of this series tonight, the statement "we ended up adding a reference counting to our models" got me curious. You obviously weren’t happy about having to do it, so what exactly motivated you to implement reference counting in Max?

The activation model I presented here works fine if there is only one "owner" that ever activates the model. But, as soon as multiple things want to activate the same model, you need the reference counting. As a simple example, what if you wanted to show the same model in two places in the UI?

This is just a little thing but when you call Activate() you immediately call your DataModel’s VerifyCalledOnUIThread(); then you set the property using this.IsActive = true which then immediately calls VerifyCalledOnUIThread(); is this an oversight or am I not getting something…seems like these VerifyCalledOnUIThread methods should only ever be called when you are changing properties. Unless you method directly manipulates the member data itself.