Thursday, May 17, 2012    
Blog  

OpenLight Blog

How to show a ChildWindow from a ViewModel using a custom Action

Aug 13

Written by:
8/13/2010 7:23 PM  RssIcon

We this is a classic example of “the early bird gets the worm.” I have been on hiatus for far too long, but have not been idle. I have actually been planning this blog entry for a few weeks, along with another one (more on this in a minute). As it turns out, Michael and Haruhiro where working on pretty much the same concept. They beat me to the finish line with there solution as you can see here: Using the “Hisowa Simple PopUp Behavior” in a DataGrid. Since I took a slightly different approach, I decided to go ahead and post this article.

Additionally, I believe that the more information is examined the best solution will be formed based on pieces of all the information. I want to be very clear, I think the solution described in Haruhiro’s article is great! This article is not being posted to “one up” or discredit Haruhiro’s work in any way and I suggest that if you have not read his post, you should.

Side Note:
The other blog entry I planned on posting is titled “Embrace your Designer” and is along some of the same topics as Michael’s post Silverlight: Why I feel “Design Is The Most Important Thing”. After seeing the articles that Michael and Haruhiro put together, I had to tell Michael to “stop stealing my ideas!” I am seriously starting to wonder if he “chipped” me when I met up with him in Cali.

With that said, “now for something completely different” … well kind of.

I have seen many example and questions regarding how to use a childwindow control when using MVVM. Most examples have either been highly criticized as not “pure MVVM” or overly complicated for a majority of applications. To me, one of the main benefits of MVVM is the simplicity that comes with the pattern. Why then would you want to implement an entire “Childwindow Service Layer” or something equally as complicated? So I started thinking “there has to be an easy way to do this in a MVVM purest fashion.”  That lead to the idea of building a behavior (or action to be specific) that could show a childwindow control. Thanks to the selection of triggers built into Silverlight, the action could be invoked several different ways with no code necessary.

For example, here is a button that is wired to a command on a ViewModel.

 

   <Button x:Name="button"
     Content="Use Command"
     Command="{Binding RequestResponseCommand}" 
     />

 

The command on the ViewModel simply raises an event:

        public void RequestResponse(object param)
        {
            if (this.RequestingResponse != null)
                this.RequestingResponse(this, new EventArgs());
        }

 

Which is handled by an EventTrigger that has the ShowChildWindowAction associated with it:

<i:EventTrigger SourceObject="{Binding}"
                EventName="RequestingResponse">
    <behaviors:ShowChildWindowAction>
        <behaviors:ShowChildWindowAction.ChildWindow>
            <controls:ResponseWindow x:Name="CommandWindow" />
        </behaviors:ShowChildWindowAction.ChildWindow>
    </behaviors:ShowChildWindowAction>
</i:EventTrigger>

So that is all that is needed to display a childwindow; however, that is not very useful in and of itself. Typically we want to know if the window was “cancelled” or “confirmed” and be able to response accordingly. We sometimes also want to have a custom childwindow and to know if any data was changed in the childwindow. This action allows for those scenarios through a set of five properties.

  • AttachDataContext: If set to true, attaches the DataContext of the control the EventTrigger is associated with. This is useful if you want to send the ViewModel (or any other DataContext) to the childwindow for binding purposes etc.
    • Note: If you attach the DataContext to the childwindow, any properties changed on that DataContext will change immediately and will not be undone if the window is cancelled.
  • ClosedCommand: The ICommand to be executed when the childwindow is closed.
  • CommandParameterProperty: The property name of the childwindow control that is used as the parameter of the ClosedCommand when executed.
  • ExecuteCommandOnCancel: Specifies if the ClosedCommand should be executed if the DialogResult of the window is “cancelled” or closed.
  • ChildWindow: Is a nested childwindow control that is created and shown when the action is invoked. This allows you to build and customize a control that inherits from ChildWindow and use it as the window to be shown.

Here is an example of what the XAML might look like for a button that calls a command only with the DialogResult of a custom ChildWindow control is true and pass the value of the Response property of the ChildWindow to the command:

<behaviors:ShowChildWindowAction
                                    CommandParameterProperty="Response"
                                    ClosedCommand="{Binding UpdateResponseCommand}">
    <behaviors:ShowChildWindowAction.ChildWindow>
        <controls:ResponseWindow x:Name="CommandWindow" />
    </behaviors:ShowChildWindowAction.ChildWindow>
</behaviors:ShowChildWindowAction>

 

Hopefully you are thinking, “Wow this is the greatest thing I have ever seen!” No? Okay, how about something more realistic like, “This seems useful, but how does it work?”

Well, the action was actually fairly simple to build. All of the properties except for ClosedCommand and ChildWindow are standard properties. ClosedCommand is a dependency property that allows for the command to be bound to an ICommand property of your ViewModel.

Note: I have included the source code for an example project so I will not show all of the code here. However, feel free to download the code and browse\use\modify it as you see fit.

In the overridden OnAttached method we handle attaching the DataContext of the associated control and wiring up an event handler to the Closed event of the childwindow if needed.

protected override void OnAttached()
{
    base.OnAttached();
    if (this.AttachDataContext)
        ChildWindow.DataContext = this.AssociatedObject.DataContext;
    if (this.ClosedCommand != null)
        ChildWindow.Closed += new EventHandler(window_Closed);
}

 

The window_Closed method is where we determine if we need to call the ClosedCommand based on the DialogResult and the ExecuteCommandOnCancel values. If so, we create the parameter for the command using reflection (This ones for you Ken! :) ), if a CommandParameter value is specified. Finally we execute the ClosedCommand. Hopefully my C# is clearer than my English:

void window_Closed(object sender, EventArgs e)
{
    ChildWindow window = (ChildWindow)sender;
    if (this.ClosedCommand != null &&
        (this.ExecuteCommandOnCancel ||
            (window.DialogResult.HasValue && window.DialogResult.Value == true)))
    {
        object param = null;
        if (!String.IsNullOrEmpty(this.CommandParameterProperty))
            param = sender.GetType().GetProperty(this.CommandParameterProperty).GetValue(sender, null);
        this.ClosedCommand.Execute(param);
    }
}

The rest of the behavior is pretty straightforward. In the Invoke method we simply call Show on the childwindow and in the OnDetaching method we remove the Closed event handler.

That about wraps it up. I intentionally chose the simplest way to provide the functionality that I required and I am sure there are MANY enhancements that can me made to this behavior. Some of which can probably be found in the HisowaSimplePopUpBehavior. Please do check out that article as well and use the foundations that have been laid here to make something awesome!

I plan on included this control in the OLGCommon project so keep your eye on that, and feel free to submit enhancements.

As always, we value your feedback!

Here is the source code for the example project and behavior: ShowChildWindowExample.zip

Updated 8/14/2010: I have upload a new example project that removed the reference to the OLGCommon assembly and changed the ChildWindow property of the behavior to be a dependency property to allow for binding.

Tags:
Categories:

5 comment(s) so far...


Gravatar

Re: How to show a ChildWindow from a ViewModel using a custom Action

this is similar to my original prototype (Not the simplified offspring in the articles) which allows Popup with it's own Viewmodel to binds it to the behavior, this was made to with to work with Michael's "Silverlight View Model Style Popup". This is why even in the "simplified" ver there is the datacontext in the return result.

oddly enough what you seem to have is what I wanted to get to as a goal when I started on my behavior and gave up on. Which was move all Childwindow references out of ANY VM. Another is you seem to use the Main View model for the childwindow, which I personally believe is a better design. I have a problem with a PopUp having it's very own view model. A Popup is usually going to do something menial such as a yes/no or select a date... so why have a VM for it if it can all be done as part of the Main View Model. Another thing is a PopUp is reliant to Main VM in someway, a datepicker popUp is going to pass it's value to the MainVM anyways why have this extra VM just for the Popup. Also I perfer to treat the Popup as a another control like buttons on the screen, not as a new window because of its function. (this is one of my main reason to even start working on the behavior). I didn't like Michael's or Pure MVVM complexity (no offense).

somehow i'm having problems building your project correct me if I'm wrong.

Also i have a question,

Is there a reason why the Childwindow property on the behavior is not a dependency, I made it a dependency so I can bind to it.

Anyways Thanks for posting your solution, I gained a lot from it. Plus it great that you have showed me a solution to a problem I have gave up on.

By haruhiro isowa on   8/14/2010 8:28 AM
Gravatar

Re: How to show a ChildWindow from a ViewModel using a custom Action

Hey Haruhiro!

Let's see if I can address everything before ADD kicks in. :)

"Which was move all Childwindow references out of ANY VM." - Yes that was a main goal of mine, partly because of the flame wars that happen when you have references to controls in a VM. This _is_ a violation of Pure MVVM and I can understand that. In the purest view, your VMs should be able to be in a library that does not have references to any of the UI assemblies. This makes it portable to WPF & SL (etc) and easily testable. So to prevent getting called a heretic by the purests, I _had_ to make the behavior completely isolated.

I agree, that most times you want the ChildWindow to modify data in the main VM; however, not always which is why I used the AttachDataContext property to turn this on or off. Why pass the datacontext, if you are just confirm a delete for example. Then you would just wire up the ClosedCommand and leave ExecuteCommandOnCancel as false.

"i'm having problems building your project correct me if I'm wrong" Argh! No you are right, I alwas reference olgcommon so i can use the BaseViewModel & DelegateCommand classes. I forgot to include the assembly and menetion that. I will pull the classes into the project and remove the reference. This will make it easier for others to build. Thanks for reporting this! :)

"Is there a reason why the Childwindow property on the behavior is not a dependency, I made it a dependency so I can bind to it."
Yes, I could not think of why you would want to do this, lol! I guess I didnt think of everything. ;) If you see a need for it, then others will and it should be updated. Thanks for pointing it out. :)

"Anyways Thanks for posting your solution"
You too! This is why being part of the dev community is so great! We can all share our ideas and get feedback and let innovation happen!

So thanks for your feedback and I hope to see you around more. ;)


By Ian T. Lackey on   8/14/2010 12:02 PM
Gravatar

Re: How to show a ChildWindow from a ViewModel using a custom Action

Very good articles an d I appreciate both approaches. It's always good to have multiple ways to accomplish a task. If nothing else it helps to expand how you think about coding. Thanks to Michael, Haruhiro and Ian for sharing the knowledge.

By Rob Brown on   8/15/2010 6:25 PM
Gravatar

Re: How to show a ChildWindow from a ViewModel using a custom Action

Thanks Rob! I appreciate you taking the time to leave your feedback!

By Ian T. Lackey on   8/15/2010 6:26 PM
Gravatar

binding to ICommand

Thanks, I will take a look. I have found out that I'm doing it "completely" wrong and I already changed my code to use commands (binding to ICommand) and remove dialog result out of the VM. The next step is to look at the Mediator pattern

By Jay Gin on   7/24/2011 2:02 AM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Add Comment   Cancel 
  
Copyright 2009 by OpenLightGroup.net   |  Privacy Statement  |  Terms Of Use