-1

As I see it, opening Views from ViewModels directly is a bad thing to do. Ideally, the ViewModels should work only with other ViewModels, and the Views should be created automatically. Where by automatically I mean without the ViewModel knowing, let alone intervening.

I know this is possible to achieve with controls that are included in other controls. The ViewModel can be linked to its View by including a DataTemplate in the resources of the hosting control:

<UserControl.Resources>
        <DataTemplate DataType="{x:Type vm:BasicSetupViewModel}">
            <vw:BasicSetupPane/>
        </DataTemplate>
</UserControl.Resources>

When the ViewModel gets binded into the hosting control, its View is rendered.

However, I'm stuck when it comes to creating window Views. If I use the same approach as with included controls, I get this exception:

System.Windows.Markup.XamlParseException: 'Window must be the root of the tree. Cannot add Window as a child of Visual.'

Which makes sense. Yet I can't think of any other way of telling the application that the window should be generated. Is there any?

zvizesna
  • 43
  • 8
  • You can see how to work with dialogs in MVVM on example of `OpenFileDialog` [A: Open File Dialog MVVM](https://stackoverflow.com/a/43756154/7713750). – Rekshino Mar 04 '20 at 13:39
  • @ASh Thanks for the tip. I have seen a few posts like this before, but I can't say I'm happy with the approach. I was hoping for a solution where the ViewModel is truly independent of the View implementation. – zvizesna Mar 05 '20 at 00:05
  • @Rekshino I have a similar problem with this as well. However, what I find interesting is a [different answer](https://stackoverflow.com/a/23303267/6105535) to the linked question. I will examine it further. Thank you. – zvizesna Mar 05 '20 at 00:13
  • Possible duplicate of [Open File Dialog MVVM](https://stackoverflow.com/q/1043918) – Peter Duniho Mar 05 '20 at 01:07
  • There are a number of variations on the "service" pattern used to do this, to separate view from view-model. See proposed duplicates. One example very much like the templating approach is a service you pass a view-model object into, and it resolves to the appropriate view. The view-model doesn't know anything about the view in that scenario. Implementing the service as an interface (obtained via factory, for example) gives you enough abstraction to be able to use the same view-models for any API; just write a service implementation appropriate to the API. – Peter Duniho Mar 05 '20 at 01:10
  • @PeterDuniho Okay, maybe I'm missing something. Where should the show method of the service be called from? Is it the ViewModel? (If so, isn't the fact that there is a **Window** service serving the ViewModel also a piece of information about the View implementation?) When should it be called? What if I create a different set of Views where I want to display the ViewModel inside a UserControl? Would it force me to create a dummy service implementation only to consume the show method calls? – zvizesna Mar 05 '20 at 02:32
  • 1
    That's a lot of questions, which is often a sign that the underlying question is simply too broad to be appropriate for Stack Overflow. And there are _lots_ of possible, valid answers to your questions. That said, to address the broader sense of your questions: one option would be, for example, a "present" method in your service. You pass it a view model, it knows what to do with it (i.e. finds the right window type creates an instance and shows the window). It should be called "at the appropriate time", which can vary widely depending on your actual business logic. ... – Peter Duniho Mar 05 '20 at 06:31
  • 1
    ... None of that would preclude using the exact same view model in other contexts. You can apply templates within existing views (windows, user controls, whatever) as needed. I would discourage having multiple window types presenting the same view model object, but you could even do that; you'd just need to decide what mechanism you'd use for the service to know how to distinguish those scenarios. – Peter Duniho Mar 05 '20 at 06:33
  • @PeterDuniho It seems I have misinterpreted the approach a bit. Thanks for your answers. – zvizesna Mar 07 '20 at 11:05
  • For anyone interested in **something "pure XAML"**, in the process of rereading all linked answers I have found this: **[~Opening Windows using XAML behavior and data binding](https://stackoverflow.com/a/15512972/6105535)** In my opinion, this is the most elegant solution to this problem I have seen so far. All information about opening windows is placed in the XAML file (not even codebehind!), so you can avoid possible unpleasant surprises and searching for services or helpers. – zvizesna Mar 07 '20 at 11:29
  • ... ideally, from the ViewModel's point of view, not thinking about it like "opening/closing windows", but rather "activating/deactivating the child ViewModel". – zvizesna Mar 07 '20 at 19:22

1 Answers1

0

When it comes to showing top-level windows in a MVVM application, you should use a window service.

You may either inject your view models with an IWindowService implementation or use a static WindowService class:

public static class WindowService
{
    public static void OpenWindow()
    {
        NewView view = new NewView();
        view.Show();
    }
}

Dependency injection is obviously preferable since it enables you to unit test the view model classes in isolation and switch implementations of the IWindowService interface at runtime.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • I've seen many people on SO using this approach. In spite of this, I can't help but I still believe that the ViewModel should not be aware of how the View is implemented. For example, what if I want to display the same ViewModel instance both inside a window and another kind of control? – zvizesna Mar 05 '20 at 00:21
  • Please see my comments below the question to get a better idea of what I find problematic. – zvizesna Mar 05 '20 at 02:38
  • @zvizesna: I don't understand what is "problematic" about using a service? – mm8 Mar 05 '20 at 13:07
  • Until this moment I have understood the service approach in a way that you pass the service to the ViewModel and use it from there. I guess [this](https://stackoverflow.com/a/25845857/6105535) [kind](https://stackoverflow.com/a/43756154/6105535) [of answers](https://stackoverflow.com/a/20134089/6105535) made me interpret it like this. But as @PeterDuniho points out in the comments, this isn't really necessary -- the service could be as well called from the View's codebehind. So after all, this is probably correct. Thanks. – zvizesna Mar 07 '20 at 11:01