5

My question involves something where thousand and one topics are created about, and if I overlooked an answer to my question, I'm sorry, but as far as I looked none really could answer my question. For example: Opening new window in MVVM WPF

The answers are okay when if you use just one WPF project (including models, vms and views) but since I'm learning how to implement MVVM the correct way (and I've read multiple times that best practice is to create seperate class lib (dll's) for model, viewmodel and a seperate gui project) that doesn't seem to work in my eyes because if I want to create an interface like IWindowService (described on previous url and also here, It's impossible to access Window or Control class because then I should have a reference to the gui project and the whole goal of the pattern is wrecked.

So my question is how to show a new window (with a new viewmodel) from for example the MainViewModel, while respecting the loosely coupled MVVM principles AND seperate projects.

More in depth example of what I'm trying to achieve:

I have following structure:

MODEL (dll project)
     Profile

VIEWMODEL (dll project)
     MainViewModel
     AddProfileViewModel

VIEW (WPF) (exe project)
     MainWindow
     AddProfileWindow

I open MainWindow and I want to press the button AddProfile, then AddProfileWindow needs to show up with the AddProfileViewModel attached to it.

tereško
  • 58,060
  • 25
  • 98
  • 150
JC97
  • 1,530
  • 2
  • 23
  • 44
  • 1
    You can use `Messenger` to send some `OpenWindow` message, and subscribe to it in View project https://stackoverflow.com/questions/16993918/mvvm-light-messenger-sending-and-registering-objects – Taras Shcherban Nov 17 '17 at 14:28
  • I've looked at Messenger but It seems a bit overkill. It seems useful tho for communicating between viewmodels so +1 for that – JC97 Nov 17 '17 at 14:50

2 Answers2

10
  • 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();
    }
}
mm8
  • 163,881
  • 10
  • 57
  • 88
  • I know this answer is old, but don't you end up needing to reference the viewmodel from the model? Because your interface needs a reference to `AddProfileViewModel` for the `OpenProfileWindow` parameter? Or should we just pass the viewmodel as `object`? – Matt Apr 20 '23 at 10:39
  • Or would it better to define `IWindowService` in the viewmodel project? – Matt Apr 20 '23 at 10:53
0

I was struggling with this and found the answer posted by @mm8 very helpful as well as a few other posting. I wasn't crazy about the idea of having to create a class (or method within a class) for each view, so I created my own variation. This is one of my first projects with WPF while attempting to use MVVM, so I'm sharing this in case it is helpful, and also to get feedback from the more experienced folks.

public class WindowDialogService : IWindowDialogService
    {
        private static readonly Dictionary<Type, Type> viewModelPairs =
            new Dictionary<Type, Type>
            {
                [typeof(DetailsViewModel)] = typeof(DetailsView)
            };

        public void ShowWindowDialog(IViewModelBase viewModel)
        {
            if (!viewModelPairs.TryGetValue(viewModel.GetType(), out Type viewType))
            {
                throw new ArgumentException("View Model not mapped", nameof(viewModel));
            }

            if (viewType.GetConstructor(Type.EmptyTypes).Invoke(new object[] { }) is Window view)
            {
                view.DataContext = viewModel;
                view.Owner = Application.Current.MainWindow;
                view.ShowDialog();
            }
        }
    }
manic_coder
  • 176
  • 7