[Update] This is part 1 of this post. Read the second post here.
At the end of building prism V2, we have played around with different application styles to see how easy it is to consume our own libraries. In this blog post, I’m going to describe my attempt at creating an outlook style application. My implementation shows the following aspects:
You can download the Source Code here:
Of course, the source code is provided ‘as is’ with no warranties.
[Update] There is a new version of this source code available in the second post for this article.
So what are my requirements for the outlook style app.
So this is my Outlook style app:
With a little bit of imagination, I hope you can see how this app could resemble outlook ;)
The following diagram shows the overall structure of the application and the most important pieces.
My shell has several regions, each brightly colored to make it clear where they are.
The ApplicationModel defines that the application has a list of UseCases. The ButtonBar (in red) is bound to the list of use cases. Each use case is represented as a button. When you click that button, the ActivateUseCase command is fired which will activate the selected UseCase.
An UseCase (like the MainEmailUseCase) implements the IActiveAwareUseCaseController and likely inherits from ActiveAwareUseCaseController. This makes it activatable (I know, it’s not a word). Lastly, the EmailUseCase itself defines which views it needs and where those views should go.
In the following paragraphs, i’m going to describe some of the moving pieces of this design.
The approach I took when creating the OutlookStyle app is to have a central ‘application model’ that knows how the application is structured. The modules also know a bit about this application model, through the IApplicationModel interface.
The ApplicationModel has a list of Main Usecases (more on usecases later), that I could bind my list of buttons to. It also has a command that can activate a UseCase when one of the buttons is clicked.
Using an ApplicationModel is kind of a double edged sword. It ties you somewhat down to a specific style of application. This makes your modules less portable, but it also makes the interaction between your modules and your shell more explicit and thus easier to understand. Using an application model is only one approach to creating an outlook style application and there are many others. But I thought it was an elegant solution and since we weren’t showing how to create an ApplicationModel in the Prism RI or the Quickstarts I thought it would be nice to show it here.
Internally, the application model uses a SingleActiveRegion to store the UseCases. This might seem strange, but a Region is nothing more than a model that can store a collection of objects that can be added, removed and activated. The SingleActiveRegion is ideal for this purpose, because it will make sure only one usecase is active at a given time. I choose not to add the region to a regionmanager, because I want the ApplicationModel to control access to this region.
My implementation for the ‘application parts’ that can be activated and deactivated are called ‘UseCaseControllers’. This were my requirements for them:
It was quite hard to find a right name for the ‘application parts’:
So called my ‘Application parts’ UseCaseControllers. In my example, the main buttons on the outlook app kind of map to the ‘main’ or initial UseCases in my application.
Why call it controller? Controller is quite an overloaded term, because of the Model View Controller pattern. However, controllers are also commonly used to coordinate some kind of process and couple several pieces together to form a single unit. So because the controller hooks several controls together to perform a single use case, I called it a UseCaseController.
The ActiveAwareUseCaseController is a handy base class that you can use to fulfill the IActiveAwareUseCaseController interface. It does the following things for you:
Adding and removing views from a region whenever something becomes active or inactive can be quite tedious. Whenever a view or usecase (or anything else that implements IActiveAware) becomes active, you probably have to find the right regions, add the views to the regions. And the same if the usecase is deactivated (but then remove the views)
To make this a bit easier, I have created the ViewToRegionBinder. This object can monitor an IActiveAware object (such as a IActiveAwareUseCaseController or a view or a ViewModel). You can register a list of objects (views or viewmodels) against names of regions so that, when the object you are monitoring becomes active, the views will be added to the right regions and removed when the object becomes inactive.
1: // Make sure the Toolbar and the mainregion get displayed when this use case is activated
2: // and removed again when it becomes inactive.
3: this.ViewToRegionBinder.Add("ToolbarRegion", this.emailToolBarViewModel);
4: this.ViewToRegionBinder.Add("MainRegion", this.emailViewModel);
Another interesting challenge I ran into was:I wanted to shows a list of buttons for each usecase in my system. But only when I click one of these buttons do I want my views to be created and initialized. You can imagine, if you have 20 of these ‘main’ usecases and each creates a bunch of views when the application starts, application load time would be dramatic.
This was one of the motivations for creating the UseCaseControllers in the first place. They are very lightweight and I could create the views I needed in the Activate() method. But then I faced an other issue. I really like to create my views and my viewmodels using Constructor Injection. However, since the objects injected the constructor would be created before the usecase would be created, not after, i had to put in some level of indirection.
This is what the ObjectFactory<Type> solves. You can express the dependency on an object in the constructor, and create the object when you need it, by calling the ObjectFactory.CreateInstance() method. You can access the instance created by the objectfactory using the ObjectFactory.Value property.
Now I didn’t want to create member variables for the ObjectFactories, because that would mean that every time I needed to access the view, i would have to go through the ObjectFactory.Value property. I really wanted to have variables of the type of the views. So I created the AddInitializationMethod, where you can add a lambda expression that will set the member variable for the views. The ActiveAwareUseCaseController will call these lambda’s just before the UseCase becomes active for the first time.
The following code snippet shows how this works in the MainEmailUseCase
1: // The references to these viewmodels will be filled when the app is initialized for the first time.
2: private EmailMainViewModel emailViewModel;
3: private EmailToolBarViewModel emailToolBarViewModel;
5: public EmailMainUseCase(
6: // Get the ViewToRegionBinder that the baseclass needs
7: IViewToRegionBinder viewtoToRegionBinder
8: // Get the factories that can create the viewmodels
9: , ObjectFactory<EmailMainViewModel> emailViewFactory
10: , ObjectFactory<EmailToolBarViewModel> emailToolBarFactory) : base(viewtoToRegionBinder)
12: // Just before the view is initialized for the first time
14: // Create the emailViewModel and assign it to this variable
15: () => this.emailViewModel = emailViewFactory.CreateInstance()
16: // Create the toolbarViewModel and assign it to this variable
17: , () => this.emailToolBarViewModel = emailToolBarFactory.CreateInstance());
Why do I like Constructor Injection? It expresses very clearly what the dependencies of my object are, in a single place. Take the previous code snippet. You can clearly see that the EmailUseCase has a dependency on the IViewToRegionBinder, the EmailMainViewModel and the EmailToolBarViewModel. Those are the only objects this class interacts with. Sure, in some cases it might seem easier to get a reference to the service locator and resolve types when you need them, but that makes it very unclear what other types your class depends on and also makes unit testing a lot harder.
As usual, the bootstrapper is the glue for all the individual pieces. In the bootstrapper, all the infrastructure pieces are registered in the right way, so the application can use them. You can see that I’m registering the types i need to the container and some of the default regionbehaviors to the RegionBehaviorFactory
In the Outlook style application, i’m also showing a bunch of other things:
Since I’ve already spent way to much time on this blogpost, i’m going to address these things in future posts.
As always, I really appreciate your feedback. Any comments or questions are more than welcome.
[Update: Also read part 2 of this post. ]