Detecting the Active View in a Prism App

A bit overdue, but a while back I wrote a post on handling graceful shutdown in a Prism application through the use of commands here and here. In those posts, I also promised to talk about detecting active views.

Baked into the Prism code since version 1 is an ability to detect when a view is active automatically as long the view is hosted in a region. It is a bit under-documented and hard to discover, but actually works quite nicely for a lot of scenarios.

Specifically in the example simple document editor I showed in the previous posts, I needed to detect when a given document was active so that I could enable or disable the global Save button in the toolbar based on the active document. That was based on state associated with the view in its ViewModel, a dirty flag indicating whether the document had been edited or not. In other contexts you might fire a pub-sub event indicating an active item, or you could change a bunch of formatting on the fly based on the active or inactive state of the view.

It actually takes very little code to get this done.

Part of the infrastructure code for Prism includes some things called region adapters. You mostly don’t have to even know they are there, but they provide the bridge between the abstract concept of a region and a specific type of container control in WPF or Silverlight. If the region that a view ends up in supports selection (specifically derives from Selector in WPF or Silverlight or is a custom container with its own region adapter that indicates selection), then when the view is selected (i.e. TabItem selected in a TabControl, ListBoxItem selected in a ListBox control, ListViewItem selected in a ListView), a behavior in the region is invoked that checks the view to see if it supports the IActiveAware interface.

IActiveAware

IActiveAware is a simple interface you can implement on your views to indicate that you want the view to be notified when it is made active or inactive. It looks like this:

publicinterface IActiveAware { ///

/// Gets or sets a value indicating whether the object is active./// /// if the object is active; otherwise .bool IsActive { get; set; } /// /// Notifies that the value for property has changed./// event EventHandler IsActiveChanged; }

The IsActive flag lets you know if your view is active, and the IsActiveChanged event will fire when that state changes.

If you implement this on your view class (typically a user control), then you need to ensure the event gets fired when the state is changed by the region behavior. Since you shouldn’t put the actual logic code of what to do when that active state changes in the view definition itself, you will also want to get that state change transmitted into the view model. If you are following the typical pattern for ViewModel, your ViewModel will be set as the DataContext for the view. You can also implement IActiveAware on the ViewModel and have a nice decoupled way to dispatch the state change into the ViewModel without coupling the view to the ViewModel.

In the sample document editor, the view implementation of the interface looks like the following:

privatebool _IsActive; publicbool IsActive { get { return _IsActive; } set { _IsActive = value; IActiveAware vmAware = DataContext as IActiveAware; if (vmAware != null) vmAware.IsActive = value; } } publicevent EventHandler IsActiveChanged = delegate { };



The setter for the IsActive property will again be called by the region behavior, and in that setter, we take care of both firing the event on the view (since it fully implements IActiveAware) and also pass the setting call into the ViewModel if appropriate by checking to see if the object set as the ViewModel implements IActiveAware as well. If it does, we just set its IsActive property to the value being set on the view itself.

Notice the syntax for the IsActiveChanged event. We call this the “delegate trick”. By declaring an empty anonymous delegate for the event, you are basically subscribing for the life of the object a single, no-op subscriber to the list who cannot be removed. That means that not only can you blindly fire the event without checking for null, but it also solves a nasty little race condition if your null check gets called right before a thread interrupt and the last subscriber is removed before your thread resumes. The question always comes up of whether that will have a performance impact. The strict answer is yes. A couple extra instructions on the stack when you fire your event. A few nanoseconds lost on a modern machine. If that is truly the performance bottleneck in your program, contact me – I want to hire you, because you are the best programmer on the planet or I don’t want to hire you because you are not building anything real.

The ViewModel implementation of the IActiveAware interface is where the real work gets done, specifically setting the enabled state of the Save command.

publicbool IsActive { get { return _IsActive; } set { _IsActive = value; SaveCommand.IsActive = value; IsActiveChanged(this, EventArgs.Empty); } } publicevent EventHandler IsActiveChanged = delegate { };



So that’s it. Simple, effective. Implement IActiveAware on your view. Dispatch the setting of the IsActive property into your ViewModel by casting the DataContext to IActiveAware and implementing IActiveAware on your ViewModel. In your ViewModel, do whatever behavior is appropriate for your view.

Any comments or questions welcome.