tags:

views:

75

answers:

4

My application has a menu option to allow the creation of a new account. The menu option's command is bound to a command (NewAccountCommand) in my ViewModel. When the user clicks the option to create a new account, the app displays a "New Account" dialog where the user can enter such data as Name, Address, etc... and then clicks "Ok" to close the dialog and create the new account.

I know my code in the ViewModel is not correct because it creates the "New Account" dialog and calls ShowDialog(). Here is a snippet from the VM:

 var modelResult = newAccountDialog.ShowDialog();
 if (modelResult == true)
 {
   //Create the new account             
 }

how do i avoid creating and showing the dialog from within my VM so I can unit test the VM?

A: 

There are different approaches to this. One common approach is to use some form of dependency injection to inject a dialog service, and use the service.

This allows any implementation of that service (ie: a different view) to be plugged in at runtime, and does give you some decoupling from the ViewModel to View.

Reed Copsey
A: 

In scenarios like this, I typically use events. The model can raise an event to ask for information and anybody can respond to it. The view would listen for the event and display the dialog.

public class MyModel
{
    public void DoSomething()
    {
        var e = new SomeQuestionEventArgs();
        OnSomeQuestion(e);
        if (e.Handled)
            mTheAnswer = e.TheAnswer;
    }
    private string mTheAnswer;
    public string TheAnswer
    {
        get { return mTheAnswer; }
    }
    public delegate void SomeQuestionHandler(object sender, SomeQuestionEventArgs e);
    public event SomeQuestionHandler SomeQuestion;
    protected virtual void OnSomeQuestion(SomeQuestionEventArgs e)
    {
        if (SomeQuestion == null) return;
        SomeQuestion(this, e);
    }
}
public class SomeQuestionEventArgs
    : EventArgs
{
    private bool mHandled = false;
    public bool Handled
    {
        get { return mHandled; }
        set { mHandled = value; }
    }
    private string mTheAnswer;
    public string TheAnswer
    {
        get { return mTheAnswer; }
        set { mTheAnswer = value; }
    }
}
public class MyView
{
    private MyModel mModel;
    public MyModel Model
    {
        get { return mModel; }
        set
        {
            if (mModel != null)
                mModel.SomeQuestion -= new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
            mModel = value;
            if (mModel != null)
                mModel.SomeQuestion += new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
        }
    }
    void mModel_SomeQuestion(object sender, SomeQuestionEventArgs e)
    {
        var dlg = new MyDlg();
        if (dlg.ShowDialog() != DialogResult.OK) return;
        e.Handled = true;
        e.TheAnswer = dlg.TheAnswer;
    }
}
Brian
That would require you to write code-behind in your view usercontrol, which isn't blasphemy, but if it can be avoided it should.
Jose
+1  A: 

I like the approach explained in this codeproject article: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

It basically creates a WPF Dialog control that can be embedded in the visual tree of another window or usercontrol.

It then uses a style trigger that causes the dialog to open up whenever there is content in the dialog.

so in you xaml all you have to do is this(where DialogViewModel is a property in you ViewModel):

<MyControls:Dialog Content = {Binding DialogViewModel}/>

and in you ViewModel you just have to do the following:

DialogViewModel = new MyDialogViewModel();

so in unit testing all you have to do is:

MyViewModel model = new MyViewModel();
model.DialogViewModel = new MyDialogViewModel();
model.DialogViewModel.InputProperty = "Here's my input";
//Assert whatever you want...

I personally create a ICommand property in my ViewModel that sets the DialogViewModel property, so that the user can push a button to get the dialog to open up.

So my ViewModel never calls a dialog it just instantiates a property. The view interprets that and display a dialog box. The beauty behind this is that if you decide to change your view at all and maybe not display a dialog, your ViewModel does not have to change one bit. It pushes all the User interaction code where it should be...in the view. And creating a wpf control allows me to re-use it whenever I need to...

There are many ways to do this, this is one I found to be good for me. :)

Jose
I like this idea. I think I will try it. Thanks.
Scott
A: 

The WPF Application Framework (WAF) shows a concrete example how to accomplish this.

The ViewModel sample application shows an Email Client in which you can open the “Email Account Settings” dialog. It uses dependency injection (MEF) and so you are still able to unit test the ViewModel.

Hope this helps.

jbe

jbe