15

Lets say I have a MainWindow and a MainViewModel, I'm not using MVVM Light or Prism in this example.
In this MainWindow I want to click a MenuItem or Button to open a NewWindow.xaml not a UserControl.
I know how to use this with UserControl to open a new UserControl in my existing Window in a ContrntControl or a Frame.

<ContentControl Content="{Binding Path=DisplayUserControl,UpdateSourceTrigger=PropertyChanged}" />

Code

public ViewModelBase DisplayUserControl
{
    get
    {
        if (displayUserControl == null)
        {
            displayUserControl = new ViewModels.UC1iewModel();
        }
        return displayUserControl;
    }
    set
    {
        if (displayUserControl == value)
        {
            return;
        }
        else
        {
            displayUserControl = value;
            OnPropertyChanged("DisplayUserControl");
        }
    }
}

In the ResourceDitionary for MainWindow I have :

<DataTemplate DataType="{x:Type localViewModels:UC1ViewModel}">
    <localViews:UC1 />
</DataTemplate>
<DataTemplate DataType="{x:Type localViewModels:UC2ViewModel}">
    <localViews:UC2 />
</DataTemplate>

The thing is that I want to open a new Window, not a UserControl. So I use some code like this :

private ICommand openNewWindow;

public ICommand OpenNewWindow
{
    get { return openNewWindow; }
}

public void DoOpenNewWindow()
{
    View.NewWindowWindow validationWindow = new View.NewWindow();
    NewWindowViewModel newWindowViewModel = new NewWindowViewModel();
    newWindow.DataContext = ewWindowViewModel;
    newWindow.Show();
}

and then a bind OpenNewWindow to a MenuItem or Button.
I know this is not the right way, but what is the right way to do this ?

Thanks!

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
xargs
  • 2,741
  • 2
  • 21
  • 33
  • Why do you think that isn't the correct way. You have an ICommand binding that is performing an action. (That seems correct?) – sircodesalot May 20 '13 at 15:23
  • 1
    it would be a problem to test that ViewModel because it would be dependent to the NewWindowViewModel class, becase I creted a instanece in the DoOpeNewWindow() method which is in the MainViewMOdel – xargs May 20 '13 at 15:48

1 Answers1

27

There are two problems you need to solve with this type of application.

Firstly, you do not want to have the View-Model creating and displaying UI components directly. One of the motivations for using MVVM is to introduce test-ability in to your View-Model, and having this class pop up new windows makes this class harder to test.

The second problem you need to solve is how to resolve the dependencies in your application, or in this instance – how to you “hook up” the View-Model to the corresponding View? A maintainable solution to this latter problem is given by the use of a DI container. A very good reference to this subject is given by Mark Seemann’s Dependency Injection in .NET. He actually also discusses how to solve the first problem too!

To solve the former problem, you need to introduce a layer of indirection to your code, to make the View-Model not dependent on a concrete implementation of creating a new window. A very simple example is given in the code below:

public class ViewModel
{
    private readonly IWindowFactory m_windowFactory;
    private ICommand m_openNewWindow;

    public ViewModel(IWindowFactory windowFactory)
    {
        m_windowFactory = windowFactory;

        /**
         * Would need to assign value to m_openNewWindow here, and associate the DoOpenWindow method
         * to the execution of the command.
         * */
        m_openNewWindow = null;  
    }

    public void DoOpenNewWindow()
    {
        m_windowFactory.CreateNewWindow();
    }

    public ICommand OpenNewWindow { get { return m_openNewWindow; } }
}

public interface IWindowFactory
{
    void CreateNewWindow();
}

public class ProductionWindowFactory: IWindowFactory
{

    #region Implementation of INewWindowFactory

    public void CreateNewWindow()
    {
       NewWindow window = new NewWindow
           {
               DataContext = new NewWindowViewModel()
           };
       window.Show();
    }

    #endregion
}

Note that you take an implementation of IWindowFactory in the constructor of your View-Model, and it is to this object that the creation of the new window is delegated to. This allows you to substitute the production implementation for a different one during testing.

Lawrence
  • 3,287
  • 19
  • 32
  • very good solution, I know that I have to deal here with DI. I wanted to ask you is tihs IWindowFactory a Factory Pattern approach. So this is basically delegated to the Factory to create that ViewModel ? – xargs May 20 '13 at 15:51
  • The purpose of the IWindowFactory is simply to delegate the actual creation of the Window to an abstraction (that can be subsequently replaced during testing). In the sense that it hides the implementation for the actual creation - yes it is a Factory Pattern approach. – Lawrence May 20 '13 at 15:56
  • 1
    @Lawrence Does that mean that you need for every window you want to create a separate Factory (i.e. `XWindowFactory` and `YWindowFactory`)? Or do you create a single factory that exposes methods like `CreateNewXWindow` and `CreateNewYWindow`? – Lennart Feb 11 '15 at 12:45
  • 1
    @Lennart: It's up to you and how you design your application. The pattern simply allows you to decouple the implementation of "open window" from how it is called. If you typically want to open `XWindow` and `YWindow` from the same view model it may well be appropriate to expose methods that do this on the same factory interface. – Lawrence Feb 11 '15 at 13:42
  • Could you put the CreateNewWindow within an IMainView class instead? – rollsch Nov 17 '16 at 05:05
  • Wouldn't the `Window` the `IWindowFactory` creates usually need the ViewModel itself as a Data Context instead of creating a new one? I'd expect `CreateNewWindow` to require an `Object` that will be applied to the `Window` as its `DataContext`. – Étienne Laneville Jun 18 '21 at 16:22