Silverlight Child Window With MEF and MVVM Light

UPDATED: I created another post that updates my approach for working with Silverlight Child Windows.

So it has been a long time since I did a pure technical post.  In fact I  have completely blown my goals for the year.  I am going to keep working towards them.  My life has been hectic since the beginning of the year and I am about to embark on a second Master’s degree.  This does not bode to well for reaching the goals I have set.  I jump on  any opportunity to play.  This is one such time.

Windows Phone 7 is due out later this year.  Microsoft was smart and decided to leverage the technical abilities of all the Silverlight developers.  So to effectively fulfill Ray Ozzie’s vision of three screens and a cloud developers are going to have to reuse as much code as possible.  One of the ways they can do that is by separating out the ViewModel into a separate project.  I have written post that demonstrated this.  One of the challenges is how to launch a child window from the view model.

In this post I will demonstrate how I have addressed the problem using MEF and MVVM Light.  I will even pass values back and forth.  I am not say that this solution will work for anyone.  In fact it may offend the MVVM purist.  I launch the child window from the parent view.  Say it ain’t so!!!  Scandalous!!!!  Before you jump all over me it works without over complicating things and I am ok with the fact that this little bit of code won’t be covered by unit tests.

The Main Page

Here is the meat of the solution:

 public partial class MainPage : UserControl   
 {       
      [Import("MainPageViewModel")]       
      public object ViewModel       
      {           
            set {DataContext = value;}
      }

              public MainPage()       
             {
                           InitializeComponent();

                           if (!ViewModelBase.IsInDesignModeStatic)           
                          {   
                                        // Use MEF To load the View Model               
                                       CompositionInitializer.SatisfyImports(this);           
                           }

                          Messenger.Default.Register<string>(this, “FromMain”,              
                                      a => { LaunchWindow(a);  });
            }

             private void LaunchWindow(string a)       
             { 
                     MyChildWindow window = new MyChildWindow(a);           
                     window.Show();       
              }   
}

As you can see I am using MEF to set my DataContext and I am using MVVM Light to listen for an event that will cause the childe window to launch. In my sample application I am passing a message from the main screen to the child window.  I will show in a bit where this little bit of magic originates.  I pass the message to the constructor of the child window.

The Child Window

Here is the code behind of the child window:

    public partial class MyChildWindow : ChildWindow   
    {       
          [Import("MyChildWindowViewModel")]       
          public object ViewModel       
          {           
                set { DataContext = value; }       
          }

                        public MyChildWindow(string message)       
                       {           
                                    InitializeComponent();

                                    if (!ViewModelBase.IsInDesignModeStatic)           
                                     {               
                                                         // Use MEF To load the View Model               
                                                         CompositionInitializer.SatisfyImports(this);          
                                      }

                                      Messenger.Default.Send<string>(message, “ChildLoad”);           
                                      Messenger.Default.Register<string>(this, “DialogResult”,              
                                                       a => { HandleDialogResults(a); });       
                          }

                          private void HandleDialogResults(string a)      
                         {            this.DialogResult = true;        }

                          private void CancelButton_Click(object sender,   
                                       RoutedEventArgs e)       
                          {            this.DialogResult = false;        }    }

Here  I am taking the text from the main page and sending it out as a message where the child window’s view model will pick it up.  I was thinking I could have exposed the text in the ViewModel with MEF.  I am not offended either way.  You can also see that we are subscribing to an event that closes the dialog.

The Child Window ViewModel

Here is the Child Window ViewModel:

    [Export("MyChildWindowViewModel")]   
    public class MyChildWindowViewModel : ViewModelBase   
    {       
          private string _childMessage;
          public string ChildMessage       
          {
               get { return _childMessage; }           
               set { _childMessage = value; }       
          }

          private RelayCommand<string> _okClicked;       
          public RelayCommand<string> OkClicked       
          {           
                get { return _okClicked; }       
          }

          public MyChildWindowViewModel()       
          {           
                _okClicked = new RelayCommand<string>(               
                         a => Messenger.Default.Send<string>                   
                         (a, "DialogResult"));

                Messenger.Default.Register<string>(this,                
                                        "ChildLoad",              
                                        a =>{ HandleMessage(a); });       
      }

       private void HandleMessage(string a)       
       {           
              ChildMessage = a;       
       }           
}

The first thing to note is the message coming through MVVM Light and getting assigned to the ChildMessage property.  You have now seen a message that was typed in a TextBox on the main page make it all the way to the child window. Now it is time to pass a message back to the main page.  So the next thing to notice is the RelayCommand.

MVVM Light provides a RelayCommand that can be bound to a button. 

<Button Content="OK"                 Width="75"                 Height="23"                 HorizontalAlignment="Right"                Margin="0,12,79,0"                Grid.Row="1" >            <i:Interaction.Triggers>                <i:EventTrigger EventName="Click">                    <ml:EventToCommand Command="{Binding OkClicked}"                                       		     CommandParameter="{Binding Text, ElementName=_dialogText}"/>                </i:EventTrigger>            </i:Interaction.Triggers> </Button>

In the initiation of the command I am creating a new message that will pass the value typed into the child window to any one how is interested.   You have already seen on subscriber to this even (think dialog close).  The beauty of the MVVM Light Messenger is that it can have multiple subscribers across multiple dlls.

The Main Page View Model

We have now come full circle.  Here is the main page’s view model:

[Export("MainPageViewModel")]   
public class MainPageViewModel: ViewModelBase   
{       
         private string _message;       
         public string Message       
         {           
                 get { return _message; }           
                 set            
                 {               
                        _message = value;               
                        RaisePropertyChanged("Message");           
                 }       
         }

        private RelayCommand<string> _launchWindow;       
        public RelayCommand<string> LaunchWindow       
        {
               get { return _launchWindow; }       
        }

        public MainPageViewModel()       
        {
             _launchWindow = new RelayCommand<string>(               
                      a=> Messenger.Default.Send<string>(a, "FromMain"));           
             Messenger.Default.Register<string>(this, "DialogResult",               
                              a => { LoadDialogResults(a); });       
        }

        private void LoadDialogResults(string a)       
        { Message = a; }   
}

Here we can see that we are getting the message from the child window (spanning dlls). So why are there two subscribers to the “DialogResult” message?  If the ChildWindowView did not handle this message then the dialog would not close. In the main page view model I am creating a RelayCommand that responds to the Launch button being clicked.  Again I am creating a message that the view handles.

My Thoughts

The circle is now complete.  We have launched a child dialog window, passing an argument.  We work in the child window and then pass information back to the main page.  There are many improvements that can be done, get rid of the magic strings, for one. I have used this and it has worked for me.  If you have other ideas I would be happy to try them.

Get the code here

P.S.  I am really digging the HTML Clipboard in the Productivity Power Tools

This entry was posted in .NET, 2010 Goals, C#, MEF, MVVM, MVVMLight, Silverlight. Bookmark the permalink.

8 Responses to Silverlight Child Window With MEF and MVVM Light

  1. singh says:

    Hello Dave,
    How to call a custom function on click of ok button in child window. thanks in advance.

    • dmd0822 says:

      Singh,

      Thanks for the comment. To answer your question the code is already doing that. By binding my relay command to the ‘ok’ button, I can run whatever code I want in my view model. Once I am done ‘handling’ the click event in the view model I fire a message (using MVVMLight) back to the child window so that it can close itself, returing ‘true’.

  2. singh says:

    hello dave,
    i m not able to get u. could u please help me. here i explain u where i struct.

    suppose i have a function : private void Savedata(){};
    how to attach this function to ur command

    EX: _okClicked = new RelayCommand(a => Messenger.Default.Send(a, “DialogResult”));

    & execute and after that control back to the main screen.

    • dmd0822 says:

      The signature of RelayCommand is just an action whereas the signature of RelayCommand is an action that takes a parameter. In my example I am doing an inline method that is taking the parameter and passing it along through messaging. In your example you could do one of two things:

      1. okClicked = new RelayCommand( () => { SaveData(); }); //(Inline)
      2. okClicked = new RelayCommand(SaveData);

      I would then fire the message to close the dialog from within the SaveData method, once you were done with whatever you were doing.

  3. singh says:

    Hi dave,
    first of all thanks to provide solution. i m able to call custom function
    okClicked = new RelayCommand( () => { SaveData(); });
    But not return to main screen . Am i missing some syntax after that?
    or it automaically return to the main screen after the function(savedate()) fire.

    • dmd0822 says:

      As I stated in my last reapons I would fire the message to close the dialog from within SaveData or within the call back from the SaveData sevice (if it is calling a service). The key is the MVVMLight messaging framework:
      Messenger.Default.Send(a, “DialogResult”));

  4. singh says:

    Thanks Dave, ur article is awesome.

  5. Pingback: Controlling Silverlight Child Windows Using MVVM | Dave's Two Cents

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s