Mvvm Show New Window from Vm When Seperated Projects

MVVM show new window from VM when seperated projects

  • Define the IWindowService interface in the model project.
  • Reference the model project from the view model project.
  • Implement the IWindowService in the WPF application (view) project.

The button should then be bound to an ICommand that uses IWindowService to open the window. Something like this:

public class MainWindowViewModel
{
private readonly IWindowService _windowService;

public MainWindowViewModel(IWindowService windowService)
{
_windowService = windowService;
AddProfile = new DelegateCommand(() =>
{
_windowService.OpenProfileWindow(new AddProfileViewModel());
});
}

public ICommand AddProfile { get; }
}

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel(new WindowService());
}
}

public class WindowService : IWindowService
{
public void OpenProfileWindow(AddProfileViewModel vm)
{
AddProfileWindow win = new AddProfileWindow();
win.DataContext = vm;
win.Show();
}
}

Opening new window in MVVM WPF

You say "creating window instance and showing window from view model is violation of MVVM". This is correct.

You are now trying to create an interface that takes a type of view specified by the VM. This is just as much of a violation. You may have abstracted away the creation logic behind an interface, but you are still requesting view creations from within the VM.

VM's should only care about creating VM's. If you really need a new window to host the new VM, then provide an interface as you have done, but one that does NOT take a view. Why do you need the view? Most (VM first) MVVM projects use implicit datatemplates to associate a view with a particular VM. The VM knows nothing about them.

Like this:

class WindowService:IWindowService
{
public void ShowWindow(object viewModel)
{
var win = new Window();
win.Content = viewModel;
win.Show();
}
}

Obviously you need to make sure you have your VM->View implicit templates set up in app.xaml for this to work. This is just standard VM first MVVM.

eg:

<Application x:Class="My.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:My.App.ViewModels"
xmlns:vw="clr-namespace:My.App.Views"
StartupUri="MainWindow.xaml">
<Application.Resources>

<DataTemplate DataType="{x:Type vm:MyVM}">
<vw:MyView/>
</DataTemplate>

</Application.Resources>
</Application>

Open a new Window in MVVM

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.

Separate project for view models to enforce MVVM: How do I open dialogs?

Why injecting class implementing IDialogService into the viewmodels abuses IoC? I think it's a good way to do this.

Don't overthink this, separating views and logic projects is a good idea, just use whatever works for you.

In my last project I have used Interaction class from ReactiveUI framework. It works well with dialogs and file pickers. I think it closely resembles your alternative, you can check the code and examples.

It's using Reactive Extensions, but you should get the idea.

What is the best way to create window from ViewModel?

i want to know what is the best and simplest method to create window from ViewModel in MVVM pattern

"The best", "the simplest" are very subjective concepts.

For some, a simple implementation may be considered difficult by someone.

This topic is very large, and for a detailed answer to such a question, you will need to write a thick textbook.

Therefore, in short, general concepts, and an example of a possible implementation.

From the ViewModel's point of view, it cannot "know" what a Window is. So asking the question "How should a VM create a Window?" - it is not correct.

The question should be asked like this: "How does the VM call an external dialogue?".

How this dialog will be implemented (WPF Window, Form or Console Input) for the VM does not matter.

A dialog is, in fact, a delegate to a method that returns a dialog result.

In the simplest case, the diaolog's result is just a bool (success / failure).

Let's say OpenFileDialog.ShowDialog returns Nullable <bool>.

In more complex cases, an enumeration is returned. Sample MessageBox.

What kind of dialogue result you need depends on the conditions of your task.

Suppose if this is an element editing dialog, then it receives an element for editing in its parameters, and it can return bool: true - editing is completed and its results need to be saved, false - editing cancellation.

Gets the ViewModel delegate, usually at the time of its creation.
This is called dependency injection.

A typical place for Dependency Injection is App.

An example will link to your previous topic WPF MVVM binding command to Datacontext inside Datagrid

using System;
using System.Collections.ObjectModel;

namespace MoldsApp
{
public class MainWindowViewModel
{
//A collection-property of type ObservableCollection is best implemented as «ReadOnly».
public ObservableCollection<Molds> AllMolds { get; }

private readonly Func<Molds, bool> editDialog;

// Constructor receiving dialog delegate
public MainWindowViewModel(Func<Molds, bool> editDialog)
{
this.editDialog = editDialog;
}

private LamdaCommand _editCommand;
public LamdaCommand EditCommand => _editCommand
?? (_editCommand = new LamdaCommand(EditExecute, EditCanExecute));

private bool EditCanExecute(object parameter)
=> parameter is Molds;

private void EditExecute(object parameter)
{
Molds molds = (Molds)parameter;

// At this point, there should be element preprocessing code.
// Let's say copying for the possibility of canceling the result of editing.
Molds moldsCopy = molds.Copy();

// Calling a dialog and getting its result
var result = editDialog(moldsCopy);

if (result)
{
// Saving the result.
// And changing the item to match the edited copy.

Save(moldsCopy);
molds.CopyValuesFrom(moldsCopy);
}
}

private void Save(Molds moldsCopy)
{
throw new NotImplementedException();
}
}
}

MainWindowViewModel Instance will be instantiated in App.
In order for the Window to get it into its DataContext, it is convenient to set it in the App resources.
To do this, let's make a simple container-class:

    public class Locator
{
public MainWindowViewModel MainVM { get; set; }
}
<Application ----------
----------
StartupUri="MainWindow.xaml"
Startup="OnStartup">
<Application.Resources>
<moldsapp:Locator x:Key="locator"/>
</Application.Resources>
</Application>

Now, in the App Code Behind, we initialize all this:

    public partial class App : Application
{
private void OnStartup(object sender, StartupEventArgs e)
{
Locator locator = (Locator) Resources["locator"];
locator.MainVM = new MainWindowViewModel(MoldsEditHandler);
}

private static bool MoldsEditHandler(Molds molds)
{
AddEditWindow window = new AddEditWindow()
{
DataContext = new AddEditWindowViewModel(molds)
};
var result = window.ShowDialog();

return result == true;
}
}

In the Main Window, we set getting the DataContext from the locator:

<Window ---------
-------------
DataContext="{Binding MainVM, Source={StaticResource locator}}">

How to open a new View in WPF using MVVM architecture?

How can I call or reference a View in the ModelView class library?

You shouldn't referernce a view. This would not only break the MVVM pattern but also cause a circular dependency between your projects.

What you should do is to define an interface in the ModelViews project. You can call it something like IWindowService. You then implement this interface in the Views project.

Please refer to my answer here for a code sample.



Related Topics



Leave a reply



Submit