Back again to my take on the Notification pattern with WPF. Last time I talked briefly about my motivation for this little series. This time we dive more into the nuts and bolts of my example implementation.
How the ViewModel notifies the UI
I defined an interface called INotificationSource for this. This
interface defines only I member which exposes a NotificationCollection.
NotificationCollection is just a standard ObservableCollection<Notification> with some additional bits in it
(such as retrieving a collection of Notifications for a given source).
By deriving from ObservableCollection you get the CollectionChangedEvent
on the collection for free.
1 2 3 4 | |
In my current app I’ve got a Layer Supertype for
ViewModels which implements this interface.
Glue code for transferring Notifications from the ViewModel into the logical tree
As I mentioned in the last post I’m using the Attached Behavior Pattern
for transferring Notifications from the ViewModel to the related
elements in the logical tree (When I say related I’m referring to the
FrameworkElements bound to my ViewModel via DataBinding). The class
responsible for the transfer is a DependencyObject derived class, called
ValidationBehavior. This class defines two attached
DependencyProperties, IsEnabled and Notifications.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
The attached DependencyProperty IsEnabled is used to attach our
behavior to the target element marked with the property. Notice the
OnValidationBehaviorEnabled handler which is called when the value of
the registered attached DependencyProperty has changed. The attached
DependencyProperty Notifications will later hold the collection of
Notifications extracted from the ViewModel by our attached behavior. By
having the NotificationCollection as an attached property you’re able to
bind against it from XAML (as we’ll see in the following post). If you
recall the last post about this topic, maybe you remember that I didn’t
actually set the attached property IsEnabled in the ViewModels XAML
file. You could configure the behavior in the related XAML file but I
didn’t want to do this for all my ViewModels. Because of this I decided
to use Styles for this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
When the Style is applied the IsEnabled attached property
will be automatically set on all elements based on that style and this
brings us into the position to hook into the elements events and access
the elements data. The OnValidationBehaviorEnabled handler is actually
straight forward.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
The anonymous event handler registered for the CollectionChangedEvent tries to get the
collection of Notifications for the FrameworkElement.
1 2 3 4 | |
As you can see this particular piece of code has a
little quirk right now. It relies on the convention / assumption that
the property on the ViewModel and the bound FrameworkElement share the
same name. It was the easiest thing to do. It would also be easy to
introduce another attached property for this in order to specify the
name of the related ViewModel property. However this wouldn’t be 100%
DRY because you’re most likely going to specify the property via the
Binding MarkupExtension, too. I’m open to suggestions of how this
correlation can be done better. The last missing code piece is the
handler which clears the Notification when it gets focus. It simply sets
the attached Notification property to null.
1 2 3 4 5 | |
Closing thoughts
I hope you’re getting a feeling for what I wanted to show with this
little post series. Today I talked mostly about how Notifications can be
transfered from the ViewModel into the WPF tree. While the code
currently has some pieces in it that imho should be refactored
(correlation of the Notifications, CollectionChangedEvent currently
reloads all Notifications), I hope you saw in this post that it’s
relatively easy to add such an ability to your app infrastructure
without a) having a base class constraint b) a lot of imperative code
and c) doing a lot of configuration. The next post will conclude this
little series mostly with XAML stuff. We’re going to cover how to
combine the different tools (DataTrigger, DataTemplates,
ControlTemplates, Converter) that WPF offers in order to fire up a
Tooltip containing all Notifications for an element.
CU next time …