IPresenter Component Model

Prerequisites

  1. Obtain and Build the Code
  2. Setting Up a Project

Referenced Assemblies

  • Caliburn.Core
  • Caliburn.PresentationFramework
  • Microsoft.Practices.ServiceLocation

Explanation

The IPresenter interface and its descendants along with the corresponding implementations found in Caliburn.PresentationFramework.ApplicationModel represent a collection of roles that appear regularly in UI architectures. You can read about many of these roles in Jeremy Miller's Presentation Patterns Wiki. Below is a list of how you might map Caliburn's interfaces and implementations to Jeremy's roles.

Role Caliburn Interface Implementations
Screen Activator IPresenter Presenter
Screen Conductor IPresenterManager, INavigator PresenterManager, Navigator
Screen Collection IPresenterHost MultiPresenterManager, MultiPresenter
Application Controller IPresenterHost, IPresenterManager, INavigator PresenterManager, Navigator, MultiPresenterManager, MultiPresenter


Note: I am painfully aware that the term "Presenter" is very misused or in the least, very ambiguous in this context. I really struggled when I was first creating these interfaces. In a future version of Caliburn, these ambiguous IPresenter interfaces will probably be renamed to more closely reflect the roles defined in Jeremy's Presentation Patterns book. Hopefully that will make their use more obvious within the framework and promote more consistent conversation on the topic of presentation patterns.

Note: One unique philosophy of presentation patterns prescribed by Caliburn stems from the fact that all these interfaces and implementations implement the base IPresenter interface. This allows all these different roles to be composed together in an infinite number of ways and for new behavior to be added by implementing these interfaces in different ways.

IPresenter

IPresenter is the most basic interface in the component model. It is implemented by all the default implementations and inherited by most of the other component model interfaces. This interface has a basic set of methods that represent a typical screen lifecycle. These methods and their intended use are described in the table below:

Method Use
OnInitialize This is called once, allowing the implementor to execute a one-time initialization sequence.
CanShutdown Called before shutdown of the implementor. If true is returned, shutdown will proceed, otherwise shutdown will be cancelled. The exception to this rule is dependent on whether the ISupportCustomShutdown interface is also implemented.
OnShutdown Allows the implementor to perform one-time shutdown logic.
OnActivate Some implementations, such as those representing the tabs in an MDI application or selectable items in a list, need to execute custom logic, such as adding/removing menus/toolbars, each time they are activated/selected. This method is called when such an event occurs.
OnDeactivate This is the opposite of Activate and is used to allow the implementor to clean up each time it is deactivated/deselected.


In addition to these methods, the IPresenter interface also has a DisplayName, which has proven to be very useful in many scenarios.

You may be wondering which methods you should override to create the open/close logic of a screen. The most accurate answer is "it depends." First, always override the methods that start with "On" unless you want to drastically change some of the underlying behavior. (The reason why all methods are virtual is for proxy support purposes.) Now, the reason I say "it depends" is because it depends on what is managing the screen: PresenterManager or MultiPresenterManager. MultiPresenterManager is going to keep screens "alive" even then they are not active. This means that it executes code in OnInitialize once when the screen is started up and OnShutdown when it is shutdown. But it will also execute code in OnActivate and OnDeactivate whenever the particular screen changes state. Imagine an MDI scenario where there are multiple screens open, but you are only looking at one at a time. The one you are looking at is Active and the others are Inactive, but they have all been Initialized. With PresenterManager,OnInitialize/OnActivate and OnShutdown/OnDeactivate are essentially the same because PresenterManger always only has one presenter open at a time. There aren't other presenters initialized but not activate.

Note: While the IPresenter represents the Screen Activator pattern it is often implemented as the ViewModel or Presenter itself rather than just managing the activation of another ViewModel. In very complex cases, it may be desirable to split these two roles.

Note: Should Caliburn's IPresenter lifecycle prove to not be fine-grained enough for your needs, examining the source code will provide a solid example from which to create your own component model in little time and with a high probability of success.

IPresenterNode

IPresenterNode inherits from IPresenter and adds one additional property: Parent. This allows the implementor to communicate directly with its immediate parent. This is most helpful in shutdown scenarios. An example of this can be seen by examining the PresenterBase.Close method.

ILifecycleNotifier

Classes which implement this interface guarantee that they will fire events related to their lifecycle. These events correspond directly to the methods defined by IPresenter. The events are: Initialized, WasShutdown, Activated, Deactivated.

IViewAware

This interface should be implemented when explicit knowledge of the view is needed. If implemented, when the view's Loaded event is fired, the IViewWare.ViewLoaded method will be called. It will be passed the view instance and the context that the view pertains to (because it is possible to have multiple view over the same model).

IPresenterHost

This interface inherits from IPresenter and is implemented by classes which which to manage the lifecycle of other presenter(s). This basically encompasses the ScreenConductor and ScreenCollection roles. The IPresenterHost has a collection of the IPresenters which it is managing called "Presenters" It also adds two important methods:

Method Use
Open Initializes the child IPresenter lifecycle and Activates it if it is already initialized. If the host tracks a "CurrentPresenter" then the previous presenter may be deactivated/shutdown before the new presenter is opened. When it has finished this process, it calls the completed callback. The callback recivies a bool indicating whether the open was successful or not.
Shutdown This starts the shutdown sequence for the indicated child presenter and calls the completed callback when the work is done. The callback receives a bool indicating whether the shutdown was successful or not. Implementors may wish to check the child presenter for implementation of ISupportCustomShutdown.

IPresenterManager

IPresenterManager is an IPresenterHost that tracks a CurrentPresenter. Whenever the CurrentPresenter is changed, the old one is deactivated/shutdown and the new one is initialized/activated.

INavigator

INavigator inherits from IPresenterManager and adds Forward/Back capability to the CurrentPresenter.

Default Implementations

PresenterBase

PresenterBase is an abstract base class from which all of the built-in presenter implementations derive. It has default implementations of the following interfaces: INotifyPropertyChanged (through PropertyChangedBase), IMetadataContainer (through MetadataContainer), IPresenter, IPresenterNode, ILifecycleNotifier and IViewAware. It also provides a number of other bindable properties and useful methods which are geared towards the common use of such a class. If you are creating your own implementations of the above interfaces, inheriting from this base class will spare you writing much boilerplate code.

Presenter

This class inherits from PresenterBase and implements the Initialize, Shutdown, Activate and Deactivate methods. Use this class for simple screens or for small parts of composite screens.

PresenterManager

This class inherits from PresenterBase and additionally implements the IPresenterManager, IPresenterHost and ISupportCustomShutdown interfaces. This is an implementation of the Screen Conductor pattern. Use this for screens or portions of screens which need to swap out content. This swappable content is represented by the CurrentPresenter property.

Navigator

Navigator inherits from PresenterManager and implements INavigator. This is most commonly used in a WPF application as the root model. This create a navigation metaphor for the UI. However, it can also be composed as part of other screens which need to maintain a history over their swappable content.

Note: Navigator is not recommended as the root node of a Silverlight application, because the browser can easily be used to maintain history by combining a PresenterManager and the DeepLinkStateManager.

MultiPresenterManager

MultiPresenterManager inherits from PresenterBase and implements the IPresenterManager, IPresenterHost and ISupportCustomShutdown interfaces. It is different than PresenterManager in that it implements the Screen Collection pattern. It is perfect for MDI scenarios because it keeps a collection of IPresenters and just swaps out which one is active at any given time. When swapping out the active presenter, it does not shutdown the previous presenter, it only deactivates it. You must call Shutdown explicitly to close a child.

MultiPresenter

This class is similar to the MultiPresenterManager in that it implements the IPresenterHost and ISupportCustomShutdown. It does not have a current presenter though. It represents a Screen Collection where all children are activated/deactivated/shutdown together.

Note: For an example of these patterns in use, see the simple ContactManager LOB Sample Application in the source download.

Last edited Oct 23, 2009 at 7:52 PM by EisenbergEffect, version 24