20 June 2007
A CSLA .NET user, Aaron Nottestad, has been helping test 3.0 and ran into some issues when using the new WPF CslaDataProvider with editable objects. Thanks Aaron! As a result, I had to do some work with CslaDataProvider, so it can manage the object’s entire lifetime from create/fetch through to save/cancel. If it can’t manage the lifetime, especially the save, then it can’t get the new object that comes back as a result of save. And if it doesn’t get that new object, then neither does data binding, so the UI controls end up editing an old instance of the object. So the CslaDataProvider now has Save() and Cancel() methods that you can call to trigger saving or canceling of the business object. Also, as the object is created/fetched CslaDataProvider automatically calls BeginEdit() on the object. This behavior is all optional, and is controlled by the ManageObjectLifetime property of the CslaDataProvider control. So if you want this behavior, you have to set that property to True. If you leave it at False (the default), then you must manage everything through code – though honestly you can’t manage editable objects through code while using CslaDataProvider. So maybe I should make the default be True? Hmm. Anyway, this was easy enough to do and worked great. The resulting forms had exactly 4 lines of code – 2 to call Save() and 2 to call Cancel(). It occurred to me that there must be a way to avoid writing those 4 lines of boring code, and Aaron put me onto the RoutedCommand concept in XAML. A RoutedCommand allows a control like a button or menu item to invoke a method on another control – purely through XAML. There are many examples out there about how this works to create standard Copy/Paste menu items that run against the current TextBox control, and that sort of thing. But none that I found which routed a command to a data provider control. The reason turns out to be that the target of a command must be a UIElement, and data providers don’t inherit from that base class. Oops… I thought I was stuck, because I really do want to route the command to CslaDataProvider in this case. However, I figured out a solution. CslaDataProvider now has a read-only CommandManager property. This property exposes a “command manager” object which does inherit from UIElement and which does handle the Save and Undo routed commands. When it is created by CslaDataProvider, it is given a reference to the CslaDataProvider control, so it simply delegates all Save commands to the CslaDataProvider.Save() method and all Undo commands to CslaDataProvider.Cancel(). With that done, you can route a command from a button or menu item (or any other valid command source) to this CommandManager object, which effectively means you’ve routed the command to the CslaDataProvider control. The CslaDataProvider gets declared like this: <csla:CslaDataProvider x:Key="ChildList"<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />