简体   繁体   English

如何使用单击处理程序和命令在WPF MVVM中打开另一个视图? (我的解决方案合理吗?)

[英]How can I open another view in WPF MVVM using click handlers and commands? (Is my solution reasonable?)

I am writing a WPF application that has two windows. 我正在编写一个具有两个窗口的WPF应用程序。

I have a MainWindowViewModel that holds two more view models: AllTagsViewModel and PlotViewModel . 我有一个MainWindowViewModel ,它拥有另外两个视图模型: AllTagsViewModelPlotViewModel

public AllTagsViewModel AllTagsViewModel { get; private set; }

public PlotViewModel PlotViewModel { get; private set; }

At the moment, I'm using this solution as a click handler in the main window: 目前,我在主窗口中将此解决方案用作点击处理程序:

private void LaunchPlotWindow_OnClick(object sender, RoutedEventArgs e)
    {
        if (PlotWindow.GlobalInstanceCount == 0)
        {
            PlotWindow plotWindow = new PlotWindow();

            PlotViewModel context = GetViewModel().PlotViewModel;
            plotWindow.DataContext = context; 

            plotWindow.Show();
        }
    }

I am also binding a command to the button. 我还将命令绑定到按钮。 The command is in the MainWindowViewModel and it instantiates a new PlotViewModel using the constructor PlotViewModel(AllTagsViewModel atvm) . 该命令位于MainWindowViewModel ,它使用构造函数PlotViewModel(AllTagsViewModel atvm)实例化一个新的PlotViewModel。

The problem with this is that the command setting the data context executes after the click handler. 问题在于,设置数据上下文的命令在单击处理程序之后执行。 this means that the PlotWindow works as expected the second time it is opened. 这意味着PlotWindow在第二次打开时按预期工作。

What is a better solution for this problem? 有什么更好的解决方案? Can I use an event to keep the AllTagsViewModel in the PlotViewModel up to date at all times with the one in the MainWindowViewModel ? 我可以使用一种事件来使AllTagsViewModel中的PlotViewModelMainWindowViewModel的一个PlotViewModel保持最新状态吗? My solution at the moment feels like a hack and very poor practice. 目前,我的解决方案感觉很糟糕,而且做法很差。

Thanks for the advice. 谢谢你的建议。

Foreword: Usually you wouldn't want to have your PlotViewModel and pass it to a window, as it makes a few things more complicated. 前言:通常,您不希望拥有PlotViewModel并将其传递给窗口,因为它会使某些事情变得更加复杂。

There are to basic approaches View-First and ViewModel First. 基本方法有View-First和ViewModel First。 In View-First you create the View (Page, Window etc) and inject the ViewModel into it (usually via constructor). 在View-First中,您创建视图(页面,窗口等)并将ViewModel注入到视图中(通常通过构造函数)。 Though this makes it a bit difficult to and pass a parameter object to it. 尽管这使将参数对象传递给它有点困难。

Which is where the NavigationService comes. NavigationService来自哪里。 You resolve the View via IoC container, then pass a parameter to the ViewModel, ie if it's a UserViewModel you'd pass the userId to it and the ViewModel will load the user. 您可以通过IoC容器解析视图,然后将参数传递给ViewModel,即,如果它是UserViewModel ,则将userId传递给它,然后ViewModel将加载用户。

The solution: Navigation Service You can either use an existing one (Prism, or other MVVM Frameworks which come with their own navigation services). 解决方案:导航服务您可以使用现有的导航服务(Prism或其他带有其自身导航服务的MVVM框架)。

If you want a own simple one, you could create an INavigationService interface and inject it into your ViewModels. 如果您想要一个简单的接口,则可以创建一个INavigationService接口并将其注入到ViewModels中。

public interface INavigationService 
{
    // T is whatever your base ViewModel class is called
    void NavigateTo<T>() where T ViewModel;
    void NavigateToNewWindow<T>();
    void NavigateToNewWindow<T>(object parameter);
    void NavigateTo<T>(object parameter);
}

and implement it like (I am assuming you use a IoC container, since IoC is a key to MVVM to key the objects decoupled. Example with Unity IoC Container) 并以类似的方式实现它(我假设您使用的是IoC容器,因为IoC是MVVM的关键,它可以用来解耦对象。Unity IoC Container的示例)

public class NavigationService : INavigationService
{
    private IUnityContainer container;
    public NavigationService(IUnityContainer container) 
    {
        this.container = container;
    }
    public void NavigateToWindow<T>(object parameter) where T : IView
    {
        // configure your IoC container to resolve a View for a given ViewModel
        // i.e. container.Register<IPlotView, PlotWindow>(); in your
        // composition root
        IView view = container.Resolve<T>();

        Window window = view as Window;
        if(window!=null)
            window.Show();

        INavigationAware nav = view as INavigationAware;
        if(nav!= null)
            nav.NavigatedTo(parameter);
    }
}

// IPlotView is an empty interface, only used to be able to resolve
// the PlotWindow w/o needing to reference to it's concrete implementation as
// calling navigationService.NavigateToWindow<PlotWindow>(userId); would violate 
// MVVM pattern, where navigationService.NavigateToWindow<IPlotWindow>(userId); doesn't. There are also other ways involving strings or naming
// convention, but this is out of scope for this answer. IView would 
// just implement "object DataContext { get; set; }" property, which is already
// implemented Control objects
public class PlotWindow : Window, IView, IPlotView
{
}

and finally you implement your PlotViewModel class and use the passed parameter to load the object 最后,您实现您的PlotViewModel类,并使用传递的参数加载对象

public class PlotViewModel : ViewModel, INotifyPropertyChanged, INavigationAware
{
    private int plotId;
    public void NavigatedTo(object parameter) where T : IView
    {
        if(!parameter is int)
            return; // Wrong parameter type passed

        this.plotId = (int)parameter;
        Task.Start( () => {
            // load the data
            PlotData = LoadPlot(plotId);
        });
    }

    private Plot plotData;
    public Plot PlotData {
        get { return plotData; }
        set 
        {
            if(plotData != value) 
            {
                plotData = value;
                OnPropertyChanged("PlotData");
            }
        }
    }
}

Of course could modify the NavigationService to also set the DataContext inside it. 当然可以修改NavigationService以在其中设置DataContext Or use strings to resolve the View/Window (such as Prism for Windows Store Apps does). 或使用字符串来解析视图/窗口(例如Windows Store Apps的Prism)。

And in the final code you open the window by calling navigationService.NavigateToWindow<IPlotView>(platId); 在最后的代码中,您可以通过调用navigationService.NavigateToWindow<IPlotView>(platId);打开窗口navigationService.NavigateToWindow<IPlotView>(platId); in your code (ie in an ICommand which is bound to a buttons Command Property in your XAML. 在您的代码中(即,在与XAML中的按钮Command属性绑定的ICommand中)。

Your approach has the possibility of creating a PlotWindow without the existing PlotViewModel if you use the CanExecute of your CreatePlotViewModelCommand . 你的方法有创造的可能性PlotWindow不存在PlotViewModel如果使用CanExecute您的CreatePlotViewModelCommand

To avoid that problem I would bind the MainWindowView to the PlotViewModel property defined inside the MainWindowViewModel . 为了避免这样的问题我会绑定MainWindowViewPlotViewModel里面定义的属性MainWindowViewModel That way you will get informed once it changes and you can set up a template creating the corresponding view. 这样,一旦更改,您将得到通知,并且可以设置一个模板来创建相应的视图。 The ViewModels could than easily be created using a command and the view will only be created if a ViewModel exists. 可以使用命令轻松创建ViewModel,并且只有在存在ViewModel的情况下才能创建视图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在WPF中使用MVVM从另一个视图打开一个视图 - How to open one view from another view using MVVM in WPF 在这种特定的WPF情况下,如何应用MVVM和命令? - How can I apply MVVM and Commands in this specific WPF situation? 我如何从 MVVM WPF 中的视图 go 到另一个视图? - How do I go to another view from a view in MVVM WPF? 如何使用 MVVM 架构在 WPF 中打开一个新视图? - How to open a new View in WPF using MVVM architecture? MVVM-WPF如何将我的视图绑定到我的Viewmodel? - MVVM - WPF How do i bind my View to my Viewmodel? WPF查看谁使用MVVM导致另一个人 - WPF view who leads to another using MVVM 如何在应用程序的框架中使用视图? (WPF MVVM) - How can I use a view from a framework in an application? (WPF MVVM) 在WPF MVVM上单击按钮,如何在命令对视图模型的引用之前或之后让我的视图执行某些操作? - In WPF MVVM on button click how to have my view do something before or after the Command reference to the view model? wpf-mvvm 如何使用 icommand 验证我的数据? - wpf-mvvm how can i validate my data with icommand? 如何使用XAML中的另一个自定义控件基类使WPF在视图中实例化一个自定义控件? - How can I make WPF instantiate a custom control in my view, using another custom control base class in my XAML?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM