mvvm-dialogs icon indicating copy to clipboard operation
mvvm-dialogs copied to clipboard

Open owner-less window using .Show() -- 'ViewModel is not present as a data context for any registered views'

Open RFBomb opened this issue 2 years ago • 6 comments

So firstly, this is my first time delving into MVVM and WPF, and I'm migrating a winforms app. I've already refactored the program's business logic into a dll, and have been building ViewModels into that dll.

For the time being here is my general setup:

Business Logic Assembly

namespace RSTCore
{
    // Contains the core logic to intialize the static dictionaries used by the rest of the dll, as well as housing the IDialogService     
    public class Program { }
}

namespace RSTCore.ViewModels
{
     //all my view models that point to business logic within this dll.
    // This assembly will be used by old winforms forms until they have properly converted, hence being a separate project.
}

UI Assembly

namespace RST
{
     // All WPF Views located within this namespace. This is a separate project in my solution that will house the updated UI. 
     // On application startup, assigns RSTCore.Program.DialogueService to a new object with an appropriate IDialogTypeLocator
}

namespace RST.WinformsUI
{
     // This is the namespace all the old windows that are eventually going to be deprecated currently reside in.
    // Once again, different project
}

The View I am trying to open:

<Window x:Class="RST.RoboQueueWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ViewModels="clr-namespace:RSTCore.ViewModels;assembly=RoboSoftwareTool_Core"
        xmlns:local="clr-namespace:RST"
        mc:Ignorable="d"
        xmlns:md="https://github.com/fantasticfiasco/mvvm-dialogs"
        md:DialogServiceViews.IsRegistered="True"
        d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type=ViewModels:RoboQueueViewModel}"
        Title="RoboQueueWindow" Height="450" Width="800">

How I'm trying to open it: Static Method within the RoboQueueViewModel class that calls the DialogueService to open a new standalone window

        /// <summary>
        /// Create a new ViewModel and start the copy operation
        /// </summary>
        public static void CreateNonModalWindow(IRoboQueue roboQueue)
        {
            var vm = new RoboQueueViewModel() { ObjectModel = roboQueue };
            Program.DialogueService.Show(vm, vm);
        }

Error Message:

Message = "View model of type 'RSTCore.ViewModels.RoboQueueViewModel' is not present as data context on any registered view. Please register the view by setting DialogServiceViews.IsRegistered=\"True\" in your XAML."

The goal: Open a new window that doesn't really need to care about any of the other windows currently open. Since this is a non-modal window, it doesn't really need an 'owner' window.

What am I doing wrong here? Can this library open up a non-modal (standalone) window? My goal (Don't know if this is proper or not) is to have the DialogueService built into the dll where the viewmodels are housed, so that the ICommands can open views as needed without caring about the actual type of the view being opened. I figured I would try this instead of writing my own WindowFactory class that utilize a bunch of interfaces.

RFBomb avatar Jun 27 '22 18:06 RFBomb

Hi there and welcome to this repository!

A maintainer will be with you shortly, but first and foremost I would like to thank you for taking the time to report this issue. Quality is of the highest priority for us, and we would never release anything with known defects. We aim to do our best but unfortunately you are here because you encountered something we didn't expect. Lets see if we can figure out what went wrong and provide a remedy for it.

github-actions[bot] avatar Jun 27 '22 18:06 github-actions[bot]

A WPF application always have a main windows, and you don't need MvvmDialogs to open the main window. The support for a main window is already there in WPF.

What MvvmDialogs is helping you with is to open other windows than the main window, whether they are modal or non-modal. Because of this, you are always required to specify the owner of the dialog you are trying to open. Often this owner is the view model representing the main window.

I think you can look at the extensive set of examples i this repository to get a feeling for how to use MvvmDialogs. If you are unable to get a grip of your issue, please create a GitHub repo with a small application, but if I see that the problem is more in understanding the usage of WPF than specifically MvvmDialogs, I would unfortunately be unable to help you.

FantasticFiasco avatar Jun 27 '22 20:06 FantasticFiasco

OK, so essentially this library ALWAYS needs to specify some owner, so the default owner should be the main ViewModel of the application. In essence, you cannot spawn a standalone (owner-less) window. I'm assuming this is enforced here so that if the top-level window is closed, all children close too, correct?

RFBomb avatar Jun 28 '22 14:06 RFBomb

Almost. How the main window is handled by WPF, and at what circumstances the MainWindow and its children are closed, can be be read about here.

FantasticFiasco avatar Jul 05 '22 18:07 FantasticFiasco

I have a followup question here. My application is a desktop app that will require some initialization before the main window opens up. During that initialization, various crashes/errors may occur. For example, the database to read from isn't available, or some required files are missing. If these occur, the dictionary objects cannot be built, and the main window will not work as it relies on those dictionaries to populate comboboxes.

Therefore, the primary viewmodel (the one for my main window) is opened AFTER my splash screen has closed. The order I had running in winforms was

  • Open up splash screen window and run in a new thread while main thread did the work to initialize everything
  • After init complete, prepare main window in background
  • After main window is instantiated, close the splash screen and open the main window
  • Program will exit once the main window has exited

If a fault occurs during initialization, a cancelation token is triggered which kills the splash screen thread and the window.

So to replicate this setup, how should I go about that? Effectively the splash screen doesn't have an owner, since the 'main window' can't instantiate yet.

RFBomb avatar Jul 27 '22 16:07 RFBomb

You can always play around with having the splash screen initially being the main window, and then open up the real main window when initializing is complete. You can read more about main windows here.

FantasticFiasco avatar Jul 28 '22 06:07 FantasticFiasco

Yea, that is actually what I'm ending up doing. I've reworked my initializing method to accept the viewmodel as a parameter to update the splash, then once its complete I swap the actual main form in place of the splash

RFBomb avatar Aug 15 '22 20:08 RFBomb