次の方法で共有


how to build an outlook style application – part 1

[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:

  • How to create application parts that can be activated and deactivated, can be put in a list, and are very lightweight.
  • How to use an Application Model?
  • How to do pure ViewModel first development. In WPF, you can apply implicit Data Templates to visualize objects, but in silverlight that doesn’t work. So I’ll demonstrate two ways to do this.
  • How to handle RegionContext in a more direct way.

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.

 

Requirements

So what are my requirements for the outlook style app.

  • Be able to show a list of buttons that can activate parts of an application (like the outlook buttons for Mail, calendar, contacts, etc.)
  • It should be possible to activate and deactivate ‘Application parts’. If an application part becomes active, It’s initial view should show up in the main area.
  • Only one ‘application part’ can be active at any point in time.
  • When a view or an application part becomes active or inactive, other views might also have to be added or removed, such as toolbars.
  • Show a list of available ‘application parts’, without really instantiating all their views immediately. Only instantiate the views the first time you show them.

Solution

So this is my Outlook style app:

image

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.

image

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.

Application model

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.

 

Showing application parts: UseCaseControllers.

My implementation for the ‘application parts’ that can be activated and deactivated are called ‘UseCaseControllers’. This were my requirements for them:

  • I want to be able to put it in a list bind a collection of buttons to
  • I want to be able to activate and deactivate it.
  • It should be very lightweight, because all available controllers will be activated at once.

Motivations for naming the ‘application parts’

It was quite hard to find a right name for the ‘application parts’:

  • It’s not a module, because a module will likely contain many application parts. Also, a module should be viewed as a unit of deployment.
  • It’s not a view, because it’s more likely a set of views, working together to fulfill a single use case. These views, can be main views, tool bars, etc..
  • I considered the term ‘feature’ for a while. However, if you have ever built installers, you’ll know that a feature is also an overloaded term. It also refers more to a unit of deployment than what we’re looking for here.

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.

ActiveAwareUseCaseController

The ActiveAwareUseCaseController is a handy base class that you can use to fulfill the IActiveAwareUseCaseController interface. It does the following things for you:

  • BeforeFirstActivation Method. Have a handy place for the first time you activate this controller. In this method (BeforeFirstActivation), you can create all your Views or ViewModels and perform any eventwiring. For example, tie any toolbar commands to your ViewModels. The ObjectFactory also helps with this.
  • ViewToRegionBinder. One of the things you are very likely to do is to add views to some regions when the UseCase becomes active, and remove them again when it becomes inactive. Instead of having to put region.Add(view) and matching region.Remove(view) calls in the activate and deactivate methods, I figured it would be handy to create one registration method that takes care of this.

Using the ViewToRegionBinder to add and remove views to and from regions

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);

Using the ObjectFactory to delay creation of your views until you actually need them

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;
    4:  
    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)
   11: {
   12:     // Just before the view is initialized for the first time
   13:     this.AddInitializationMethods(
   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());
   18: }

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.

 

Bootstrapper

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

 

What’s next?

In the Outlook style application, i’m also showing a bunch of other things:

  • How to do ViewModel First development in an elegant way.
  • How to use RegionContext in a bit easier way
  • How to show views in a popup in a robust way. Note, in the current version, this is still work in progress.

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.

Happy coding!

_Erwin

[Update: Also read part 2 of this post. ]

Comments

  • Anonymous
    March 02, 2009
    Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Anonymous
    March 02, 2009
    Erwin has posted on building an Outlook style application .&#160; He shows a number of nice features

  • Anonymous
    March 06, 2009
    Hi, Nice post! But often we need specific region configuration for each application section. For example in email section we have only main region, which is streached on whole screen width, but in daily task manager we have 3 regions (left (10%),main(80%),right(10%)) That is why we need to load some kind of region templates or have an ability to dynamically add nested regions. What u think about it? Is it possible with current version of Prism? Thanks, Alexey Zakharov.

  • Anonymous
    March 06, 2009
    Yes this is possible. In too many ways than i can describe. The easiest way is to have the shell expose only the regions are required by ALL views. So have the daily task view expose three other regions which you can then populate with other views. An other way is to make the regions of the shell resize based on their contents. (an empty region should then be invisible) And you can also create 'template' views. A template view will then have the left, main and right region, but each version of the template view will display them differently. Just make sure you add the view in the 'activate' method and remove the template view in the deactivate.

  • Anonymous
    March 06, 2009
    Thanks Erwin. I will try it. =)

  • Anonymous
    March 17, 2009
    Hi, Making a video tutorial on how implement a good navigation practice will be really useful. How can add transitions between 2 views (for example a fade transition before moving to another page) ? PRISM V2 is really great, but even if you provided a lot of quick start in the download, I think you still need more videos like the one you did in 4 parts. That was amazing. Also, why don't you post anything on the Silvelight community website ? It's great to learn good ways to implements a code and sharing a framework thanks for all your works !

  • Anonymous
    March 18, 2009
    Navigation.. That's one of the things we would have liked to tackle in Prism V2, but unfortunately we didn't have time to do. If I have time, I might put up a simple demo on how to do some basic navigation scenario's in the next couple of weeks. However, building a generic navigation solution that can tackle a lot of scenario's is a LOT of work. More work than i have spare time anyway ;) Hopefully, if we get to do a Prism V3, we can tackle some navigation scenario's.  

  • Anonymous
    March 20, 2009
    Hi! Thanks for this great sample application! I implemented this in my application and changed the SingleActiveRegion to a Region. My UseCases are added to a TabControl. The User is able to close these tabs. I tried to implement a deactivation of the UseCases, but I get the error, that the RegionManager does not contain the Regions in the removed view when closing tabs. Will you show how to deactivate UseCases when you have some time? Thanks, Woggly

  • Anonymous
    March 23, 2009
    "...if we get to do a Prism V3..." What do you mean "IF"?

  • Anonymous
    March 31, 2009
    Hi Erwin, Following the new release of SL 3 will you adapt Prism V2 to this release ? for example, back to the navigation Framework, SL3 provides Frames and Pages. The region Manager is therefore becoming somehow obsolet ? Many thanks Regards

  • Anonymous
    March 31, 2009
    And yes, by the way, commenting Gustavoc => " ... If we get to do a Prism V3 ..." I do agree, what do you mean by "IF" I've been fighting to use the PRISM framework as the official guidance from Microsoft.... I hope that you guys are really willing to provide us with more updates on this framework and not just abandon this project as a V2 has been written..... I will really look stupid if there is no more follow up

  • Anonymous
    March 31, 2009
    It's good to hear that you guys feel so passionate about Prism. We have talked about doing a Prism V3 and I certainly hope there is going to be one but unfortunately, i can't guarantee it. When our plans become more concrete, i'll definetly let you know. We always try to invest our time in area's we feel are the most important. As silverlight and WPF is very important to us, i think there is a good chance.

  • Anonymous
    March 31, 2009
    Hi Woggly, The reason you can't deactivate the region through the regionmanager is because this region is not registered to any regionmanager. It also doesn't have a name... In my implementation of this app, i decided that I wanted an application controller to be responsible for activating and deactivating the use cases. That's why i didn't use a 'normal' region that I declared in xaml. There are several ways you can make the region you've created in the regionmanager. The easiest is: Give the region a name (region.name property) and then add the region to a regionmanager. RegionManager.Regions.Add(region, region.Name); Hope this helps. Erwin

  • Anonymous
    March 31, 2009
    Ok, I enquired around a bit with regards to our planning. As far as I can tell now, we are planning to do an other version of Prism, where we're going to rev Prism for the next version of Visual studio and .Net 4.0 and Silverlight 3. We don't know yet when this is going to occur, but I'll keep you posted. Hope this helps, Erwin

  • Anonymous
    April 01, 2009
    Реализация модели отношений master-details при написании приложения с использова

  • Anonymous
    April 07, 2009
    Any updates as far as loading "sub" views (or UseCases) into a region from within a module? For example: a module loads a toolbar, then the user clicks a button on the toolbar which loads the corresponding view into the main region (all from within a module).   I see that this work was started in this sample but not finished?  It appears that the code that handles this (ShowUseCase) says "Work in progress...".  As far as I can tell, the ShowUseCase method is never called either.   I'm trying to model an application around this sample, but I really require the ability load/unload multiple views within a module from a toolbar or menu.  I'm not having any luck figuring out how to do this within the framework presented in this sample. Any help would be appreciated. Thanks! -Patrick

  • Anonymous
    April 08, 2009
    First of all,  I really like these PRISM tutorials so keep them coming. I currently working on a new project that is planned to use PRISM and investigating technologies for it. One of things, that I would be intersted in is the recommend method for handling dialog screens as modules. What I would like to see is similar tutorials using Expression Blend 2/3 in conjunction with PRISM setup.  This is what I am interested in enterrprise applications at work.  What would be interested is steps in making a PRISM based User interface from ground up using Blend that once done could be handled off to staff of Services API developers to developed the services behind the scene.  Ideally it would be nice if all interface development on client side will be done in Blend and API done using Visual Studio 2008. I am also be interested if anything in Prism will allow the modules to be in seperate domains - this is so if one screen in application. crashes it does not crash the reset of applications.  This may not be the intent of PRISM.  Is it invision to have multiple application using same shell - or should each application have its own shell. Anyway thanks for information. Stewart

  • Anonymous
    April 08, 2009
    Hi steward, There is a section in the prism docs on designer - developer interaction. I personally feel that it's better to have a designer and a developer work together to get the UI done, rather than having a designer create the whole UI up front. Because inevitably, while building the api you are going to run into issues witih the design and you need the designers input to solve them. I actually would argue that some of the code should be built BEFORE the designers come into play. For example, it's nice to have viewmodels in place, so the designers can use these to populate their views in blend with code. What do you mean with dialog screens as modules? IMHO dialog screens and modules have different levels of granularity. We explicitly did not tackle loading modules in different app domains, to keep the design as simple as possible. However, if you feel that you need that kind of functionality, you can write your own ModuleLoader that would do that. This will mean however, that all the communication between modules will have to deal with cross appdomain communication. I recommend carefully weighing if that cost is worth the effort... _Erwin

  • Anonymous
    April 08, 2009
    Hi steward, There is a section in the prism docs on designer - developer interaction. I personally feel that it's better to have a designer and a developer work together to get the UI done, rather than having a designer create the whole UI up front. Because inevitably, while building the api you are going to run into issues witih the design and you need the designers input to solve them. I actually would argue that some of the code should be built BEFORE the designers come into play. For example, it's nice to have viewmodels in place, so the designers can use these to populate their views in blend with code. What do you mean with dialog screens as modules? IMHO dialog screens and modules have different levels of granularity. We explicitly did not tackle loading modules in different app domains, to keep the design as simple as possible. However, if you feel that you need that kind of functionality, you can write your own ModuleLoader that would do that. This will mean however, that all the communication between modules will have to deal with cross appdomain communication. I recommend carefully weighing if that cost is worth the effort... _Erwin

  • Anonymous
    April 09, 2009
    When I compile your project I got the following error Error 1 The type 'System.ComponentModel.INotifyPropertyChanged' is defined in an assembly that is not referenced. You must add a reference to assembly 'System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'. C:Usersv-payongDesktopPrismOutlookOutlookStyle.InfrastructureModelVisualizationVisualizingRegion.cs 19 18 OutlookStyle.Infrastructure Anything I missing?

  • Anonymous
    April 09, 2009
    Erwin, Thanks for the response. On the Domain issue,  you basically confirm what I was thinking about PRISM and I believe it not recommend to have more than one enterprise application in the same shell.  Is this true? On the Blend issue, basically my thought is we need a basic code layout and orgranization - so that designers could work.  Currently our workforce UI designers are the coders - but  this is primary because existing products are WinForm based and Blend leads to designers and coders. I am desiring an code organization so that we could have seperate designers working with screens one day and looking for recommendations on best way to handle this.   I am right now converting a WPF prototype into PRISM layout for this possibility. On the dialog issue, I issue, I basically talking about recommended usage of sperate Windows in PRISM stuff.   I like the ideas of regions and can we create a region as deperate window. On the region stuff can we create regions inside of regions. Stewart

  • Anonymous
    April 09, 2009
    I been working with Regions and this stuff is awesome.  I am curious if there is a way to programatically hide and show and region. I likely could do it the way I did it prototype with setting size and visibility on control.  But I wonder if there is method in PRISM.

  • Anonymous
    April 29, 2009
    A while ago, I put an example application on my blog on how to build an outlook style application . The

  • Anonymous
    June 20, 2009
    The comment has been removed

  • Anonymous
    June 21, 2009
    The comment has been removed

  • Anonymous
    June 24, 2009
    Can you share a sample code which makes a region as SingleActiveRegion in silverlight. I have a region that needs to be host more than one view based on some events.