(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

A ChildWindow management service for MVVM applications

(16 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
10 comments   /   posted on Nov 17, 2009
Tags:   mvvm , prism , andrea-boschin

This article is compatible with the latest version of Silverlight.

Programming Model-View-ViewModel applications with Silverlight is not always an easy task. In many cases there are some architectural obstacles that prevent from correctly applying the pattern. The ChildWindow control is affected by this problem because it cannot be opened or closed through a property bound to the ViewModel. In this article I will show how to use the EventAggregator and a Unity Service with Prism v2 to decouple the ChildWindows from the ViewModels and let the MVVM architecture to work effectively.

Download source code

Introduction

Popups, overlays, and windows are common patterns to display and collect information to or from an user in an application. In the past, this kind of elements have been deprecated by the web application model because they are inconsistent and difficult to handle in a simple HTML page, but with Dynamic HTML, Ajax and recently with Silverlight they become again an option while writing an application.

Silverlight 3.0 has introduced a new control - the ChildWindow - that lets the developer easily create effective popup windows, with a model very close to what we may experience in programming a Windows Forms application. A ChildWindow is quite similar to a UserControl, with a XAML file and a code-behind, but has a subtle difference. It does not need to be inserted into the Visual Tree of a page but may be instantiated and opened from the code-behind on top of what we are currently showing. Here is a sample code showing how to open a ChildWindow.

 MyChildWindow window = new MyChildWindow();
 window.Closed += new EventHandler(window_Closed);
 window.Show();

Popping up a ChildWindow in this way is comfortable while you are writing a normal application - using the code-behind - but it is very annoying if you are implementing the Model-View-ViewModel pattern. Calling a method to open the window and waiting an event to be raised to know if it has been closed, is a model that goes on the opposite direction of what you expect to do in a MVVM application. 

If you put the call in the code-behind you are violating one of the fundamental precepts of the pattern because you should put the interface logic in the ViewModel and leave the View clean to let the Designers make them work without knowing about the code to open the popup. The code-behind is strongly connected to the View and then it is not a good place for the purpose.

It may seem that a good place for doing the call to open a popup is the ViewModel but if you put the code in the ViewModel you are strongly connecting the UI to the Logic and from a testing perspective you are doing something that you will be unable to test.

While opening a popup requires a call to a method, there is not a valid solution to do it without creating a flaw in the MVVM pattern and coupling what is expected to be separated. So probably the best solution is to take away the logic required to open the popup and create a service responsible to manage the popup from the moment it is opened to the moment it has been closed. You just have to raise an event and the service receives it and opens the new ChildWindow.

The three cases of a popup

What I have suggested may seem to be simple but if you  analyze more deeply the solution you will quickly understand that there are different conditions to handle. In my experience when you decide to pop up a window you are in one of those three cases:

a) You need to give a feedback about an action, an error or something similar. You may also popup a window to do more complex operations, but in this case the popup is something you can forget after you have popped it out. The window shows a message, lets the user do something,  and you do not need to know anything about when the user has closed it.

b) You are requesting a confirmation. This is the case of a simple window asking about the deletion of a record. In this case you need to know the answer of the user but the answer is specific to the calling code. No one needs to know about the deletion, but the calling ViewModel that has to delete the record.

c) You have to collect information that needs to be handled by multiple ViewModels. An example is a search window where you ask the search keys and then you can have a View showing the keys, one showing the results and so on. When the user hits the OK button the collected data has to be spread across all the viewmodels.

In the next paragraph I will show you how I solve these cases in my real world applications. The solutions do not pretend to be final, but are what lets me be productive and have a good implementation of the MVVM pattern.

Silverlight, Prism and Services

In a previous paragraph, I've anticipated that the solution needs to create a Service responsible of handling one or more popups. With the word "service" I identify a simple class that implements the Singleton pattern. This class is unique across the Application Domain.It is instantiated at the start of the application and remains idle listening for incoming events.

In my applications I'm used to adopting the Composite Application Guidance for Silverlight, also known as Prism. The library, originally published on codeplex (http://www.codeplex.com/prism), is now available to download on MSDN here. The library is open source and is currently developed by the Microsoft Pattern & Practices team.

In Prism there are two things that may help you create the Service and communicate with it. The first is Unity - a simple and effective IoC container responsible of managing instances and lifetimes. With Unity you can register a class and give it a LifetimeManager to handle when the class is created and destroyed. Using a ContainerControlledLifetimeManager transforms a class in a Singleton service.

To register a Service you have to start the Unity container using a Bootstrapper. The Bootstrapper class is instantiated and started in the App.xaml.cs:

 private void Application_Startup(object sender, StartupEventArgs e)
 {
     new Bootstrapper().Run();
 }

In a simple application, the Bootstrapper is a good place to initialize the services. You need to take a reference to the IoC container - it is a Service itself and is instantiated when you run the Bootstrapper - and register an instance of the class that you want to run as a service. It is important here to register an instance and not a type because we need the Service to immediately start its lifetime:

 /// <summary>
 /// Initializes the modules.
 /// </summary>
 protected override void InitializeModules()
 {
     // get the IoC container
     
     this.TheContainer = 
         ServiceLocator.Current.GetInstance<IUnityContainer>();
   
     // Register the service
  
     this.TheContainer.RegisterInstance(
         typeof(DialogManager), 
         new DialogManager(),
         new ContainerControlledLifetimeManager());
   
     base.InitializeModules();
 }

When the Service has been registered and then started, it has to listen for events raised for the ViewModels. Here Prism helps you with another useful tool called EventAggregator. The EventAggregator is another service that is responsible for taking incoming events from the various ViewModels and forwarding them to other listening ViewModels or services. Here, an actor subscribes the event and another one publishes it attaching a payload to it that will be received by the subscriber. So for opening a window someone has to write this code:

 public class ErrorEvent : CompositePresentationEvent<Exception>
 { }
  
 // ... then ...
  
 this.TheAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
 this.TheAggregator.GetEvent<ErrorEvent>().Publish(ex);

The ErrorEvent class represents the event to someone that can subscribe. It derives from the CompositePresentationEvent class and has a payload of type Exception. When we catch an exception we can simply raise the event in a way to popup an error dialog.

Handling the three cases

After publishing the event, we expect that it is routed to the listening service. The service subscribes the event and then it opens the popup. The code I've proposed in the last box refers to the simplest of the cases, when the popup does not need to return anything to the caller. In this case the service simply opens the popup:

 public DialogManager()
 {
     this.TheAggregator = 
         ServiceLocator.Current.GetInstance<IEventAggregator>();
  
     this.TheAggregator.GetEvent<ErrorEvent>()
         .Subscribe(ShowErrorDialog);
 }
  
 public void ShowErrorDialog(Exception ex)
 {
     ErrorDialog dialog = new ErrorDialog();
     dialog.Message = ex.ToString();
     dialog.Show();
 }

When I need to receive a simple response (simple as yes or no), I need to use only in the calling ViewModel. I'm used to passing a delegate to the service it will callback. This may be a bit complex to understand but I found it is useful to leave the code simple and compact thanks to lambda expressions. The problem here is that the EventAggregator does not have a way to answer only to the publisher of the event so I work around this limitation by passing a reference to a method which the service will call:

 // raise the event and handle the answer
  
 this.TheAggregator.GetEvent<ConfirmRequestEvent>()
     .Publish(
         new ConfirmMessage("Do you confirm?",
         r =>             // <=== this is an Action<bool?> delegate
         {
             if (r == true)
             { /* user confirmed */ }
         }));
  
 // handle the service
   
 public void ShowConfirmDialog(ConfirmMessage message)
 {
     ConfirmDialog dialog = new ConfirmDialog();
     dialog.Message = message.Message;
     dialog.Closed += 
         (s, e) => { message.Callback(dialog.DialogResult); };
     dialog.Show();
 }

Here, the payload is a class that transports the message to display and an Action<bool?> that is the callback method to call. By using a lambda expression the code remains very compact.

Finally, when you need to have a result spread to all the listening ViewModels you can simply raise an event from inside the service and it will be handled from every subscriber. In my example, I simulate a color selector window where you can choose a color to give to the user interface. Here is the service method:

 public void ShowColorSelectorDialog(object dummy)
 {
     ColorSelectorDialog dialog = new ColorSelectorDialog();
     ColorSelectorViewModel vm = new ColorSelectorViewModel();
     dialog.DataContext = vm;
     dialog.Closed +=
         (s, e) =>
         {
             if (dialog.DialogResult == true)
             {
                 this.TheAggregator.GetEvent<SetColorEvent>()
                     .Publish(new SetColorMessage(vm.Number, vm.Color));
             }
         };
     dialog.Show();
 }

In this snippet I assign a ViewModel to the View because the window needs a kind of logic (more complex that a simple "yes or no") to handle the user interaction. When the user closes the window I take the result from the ViewModel and not from the View because in my opinion this is a responsibility of this class.

Conclusion

Like every MVVM application there is a lot of code to write to make them work and it is difficult to show every line of code in an article. This is the reason why I've included a working sample where you can see all of the three solutions implemented and working. If you have any questions do not hesitate to contact me on my weblog.

Andrea Boschin
Most Valuable Professional - Silverlight
Freelance Consultant & Contractor
http://www.silverlightplayground.org


Subscribe

Comments

  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by Tom on Nov 17, 2009 16:08
    Excellent
  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by jsp3536 on Nov 17, 2009 16:54
    I am not sure why people have problems using the code behind in the MVVM applications.  The pattern never states you cannot use the code behind.  Opening a child window is a view specific operation which I think is perfectly acceptable to be in the code behind.  I think people waste a lot of time trying not to write any code in the code behind.  I think the important thing to remember is the whole reason to use the MVVM pattern is so you can write unit tests against theViewModel. 
  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by Andrea Boschin on Nov 17, 2009 22:41

    @jsp3536: I do not think the only reason to use MVVM is to write unit test against your ViewModels. This is for sure a big advantage of the pattern but I think the other big advantage is enabling designers to manipulate the user interface without having to know anything about the logic. Opening a ChildWindow from the codebehind violate both these constraints: You need to have designers capable of understanding the code in the codebehind and you are writing some code you will never test.

    I agree, there is some cases where writing code in the codebehind is required, and with Silverlight these cases are not unusual, but trying to avoid the use of the codebehind is a must everytime it is possible. IMHO.

  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by Imran on Nov 19, 2009 22:43
    The main reason for using MVVM model is to separate UI from Logic and in all instances I would say opening/closing an ChildWindow is UI work than logic and I have to agree with jsp, I have no idea why people want to do everything in ViewModel and sometimes even stretching the ViewModel to support UI operations, which I don't' agree to. I use a simple distinction while dealing with what should be taken to ViewModel and what should be left in code behind, if some operation that can't be databoud and would require it to work on the UI thread then I leave it in code behind.  

    I am not saying this lightly, I have recently struggled with the same problem where error messages are generated in VM and I had to display them in ChildWindow, and I know how much painful it can get.

    Excellent post btw, I don't normally rely on Prism, sometimes it feels too heavy for simple applications. 

  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by sacha barber (C# MVP) on Jan 10, 2010 11:57
    I did something simliar for WPF a long time ago in my own MVVM framework for WPF : http://cinch.codeplex.com/

    And more recently I wrote one (yesterday as I have only just started looking into SL again, I prefer WPF proper) which deals with ChildWindow and also how to show it Modally.

    http://www.codeproject.com/KB/silverlight/SL4FileUploadAnd_SL4_MVVM.aspx

    Mine does not use Unity for SL (though the WPF one was DId in using Unity), but it does offer:

    1. Taking any object as a DataContext for the ChildWindow
    2. Callbacks for Dialogresult
    3. Modeless / Modal support
    4. Unit test version of the service

    Love to know what you think



  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by jay on Mar 30, 2010 19:25

    HI,

    I am QA tester, and I having problems finding a tool for automating test scripts for a Silverlight app.

    The closest I've found is WebUii by Terelik; however, their tool cannot recognize Chilhwindows; it only recognizes elements that are copied into the visual tree.

    Question: does anyone know of any other tool that I can use for QA automation?

    Really appreciate your help.

    tks

  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by Brent on Aug 14, 2010 01:46

    Jay, the Coded UI testing system in VS2010 will have support for Silverlight app testing (it's on their roadmap).  Until then, Telerik's is the best I've found, too.

  • -_-

    RE: A ChildWindow management service for MVVM applications


    posted by Dipen on Oct 29, 2010 14:23
    From my view simple open the Childwindow from CodeBehind and be happy. There is no hard rule you cannot do that using MVVM.
  • DubiMartin

    Re: A ChildWindow management service for MVVM applications


    posted by DubiMartin on Sep 16, 2011 13:10
    Very useful article! Thank you very much!
  • TauseefKhan

    Re: A ChildWindow management service for MVVM applications


    posted by TauseefKhan on Dec 27, 2011 12:38

    NO,NOT AT ALL,DOES NOT MAKE A SENSE , JUST TO SHOW CHILD WINDOWS I ADOPT PRISM INTO MY APPLICATION, FAILED.PURPOSE SOMETHING SIMILAR WHICH IS WITHOUT PRISM .



Add Comment

Login to comment:
  *      *