canton7 / Stylet

A very lightweight but powerful ViewModel-First MVVM framework for WPF for .NET Framework and .NET Core, inspired by Caliburn.Micro.
MIT License
990 stars 143 forks source link

Launch a UserControl view from the corresponding ViewModel #105

Closed fengfranklu closed 4 years ago

fengfranklu commented 4 years ago

Dear Antony, most of my views are UserControls except the MainShell which is a Window. During the development, sometimes I like to launch a UserControl individually to focus on just that item. So I am trying to use the WindowManager.showWindow and feed it with a ViewModel to launch the View. But it seems can only launch a window. Do you have a way to launch a UserControl or let me know how to find the view from the ViewModel? I know how to launch a UserControl directly. Many thanks. Frank

canton7 commented 4 years ago

You can easily create a Window which has a corresponding VM. The VM just owns a child VM, and the Window just shows the View for that child VM. Then do e.g.WindowManager.ShowWindow(new WindowViewModel(new MyUserControlViewModel)) (or wrap that up in an extension method).

Then you can control exactly how the UserControl is shown in the window: where window title comes from, how it's sized, etc.

Make sense?

fengfranklu commented 4 years ago

This should work. Make sense. I will give it a try.

fengfranklu commented 4 years ago

This seems to be a little complicated. Because the WindowViewModoel has a MyUserControlViewModel as an input, so every time I try to launch a new user control, I need to mod this WindowViewModel to input the new UserControlViewModel.

What I used to do is to create a set of methods and each method will launch a user control, something like (if using stylet):

var vmfactory = IoC.Get<AssignGroupViewModelFactory>();
  var vm = vmfactory.CreateAssignGroupViewModel(lci);   //(lci some runtime data)
_windowManager.ShowWindow(vm);                                 // I wish something will work here

Before Stylet, I would just new up the ViewModel and then create the view from ViewModel Then launch it. like:

var view = new AssignGroupView(viewModel);
NavigationController.NavigatePopMaxToUserControl(view, "Assign Group View");

I don't mind creating a window to host a user control, but to create a window for each user control I am trying to launch seems too much work. Also, the code to feed runtime data will have to go to the main window's ViewModel code and could be potentially different for each user control.

I am not sure if extension method will help here. Am I missing something?

fengfranklu commented 4 years ago

Here is the code I used to launch a UserControl

public static void NavigatePopMaxToUserControl(UserControl target, string title)
        {
            var popwindow = new Window
            {                
                Title = title,
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                WindowState = WindowState.Maximized
            };

            var mainWindow = Application.Current.MainWindow;
            if (mainWindow != null && mainWindow.IsLoaded) popwindow.Owner = mainWindow;
            if (target != null) popwindow.Content = target;

            popwindow.ShowDialog();
        }

If I know how to call the function that finds the view from ViewModel and bind the content, I can use the above code to accomplish the task. Thanks!! :-)

fengfranklu commented 4 years ago

I think I may be solved this problem. the following example method successfully launched a UserControl view.

 public void AssignGroupView()
        {            
            var lci = new LaunchClass()
            {
                LaunchClassId = 1
            };

            var vmfactory = IoC.Get<AssignGroupViewModelFactory>();
            var vm = vmfactory.CreateAssignGroupViewModel(lci);
            var viewManager = IoC.Get<IViewManager>();
            var view =(UserControl) viewManager.CreateAndBindViewForModelIfNecessary(vm);
            NavigationController.NavigatePopMaxToUserControl(view, "Assign Group View");
        }

Am I using the right function to get the view? this is just for testing a view in the development stage.
In the application itself, I understand I should use conductor/screen with Child ViewModel as parameters to show child views. Thanks!

canton7 commented 4 years ago

I was thinking just:

WindowViewModel.cs:

public class WindowViewModel : Screen
{
    public IScreen Child { get; }

    public WindowViewModel(IScreen child)
    {
        Child = child;
        Child.ConductWith(this);
    }
}

WindowView.xaml:

<Window x:Class="Stylet.Samples.Hello.WindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:s="https://github.com/canton7/Stylet"
        Title="{Binding Child.DisplayName}" Height="450" Width="800">
    <ContentControl s:View.Model="{Binding Child}"/>
</Window>

Then you can write:

this.windowManager.ShowWindow(new WindowViewModel(new TestViewModel()));

Or you can create an extension method:

public static class WindowExtensions
{
    public static void ShowUserControlAsWindow(this IWindowManager windowManager, object viewModel)
    {
        windowManager.ShowWindow(new WindowViewModel(viewModel));
    }
}
fengfranklu commented 4 years ago

Thanks Antony!!! This looks great. Appreciate!