我怎样才能打开使用点击处理程序和命令在WPF MVVM另一种观点? (是我的解决方案是否合理?) [英] How can I open another view in WPF MVVM using click handlers and commands? (Is my solution reasonable?)

查看:145
本文介绍了我怎样才能打开使用点击处理程序和命令在WPF MVVM另一种观点? (是我的解决方案是否合理?)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写有两个窗口的WPF应用程序。

I am writing a WPF application that has two windows.

我有一个 MainWindowViewModel 持有两个视图模型: AllTagsViewModel PlotViewModel

I have a MainWindowViewModel that holds two more view models: AllTagsViewModel and PlotViewModel.

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();
        }
    }



我也绑定一个命令按钮。该命令是在 MainWindowViewModel ,它使用构造 PlotViewModel(AllTagsViewModel ATVM)实例化一个新的PlotViewModel。

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).

这里的问题是,该命令设置数据上下文单击处理程序后执行。这意味着 PlotWindow 如预期打开它第二次工作。

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.

,这是什么更好的解决办法问题?我可以用一个事件来保持 AllTagsViewModel PlotViewModel 最新的在任何时候都与一个在 MainWindowViewModel ?我当时的解决方案,感觉就像一个黑客和实践非常差。

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? My solution at the moment feels like a hack and very poor practice.

谢谢你的建议。

推荐答案

前言:
通常你不想让你的PlotViewModel并把它传递到一个窗口,因为它使一些事情变得更加复杂。

Foreword: Usually you wouldn't want to have your PlotViewModel and pass it to a window, as it makes a few things more complicated.

有基本的方法查看一和视图模型第一。在View-首先创建视图(页,窗口等)和注入视图模型进去(通常是通过构造函数)。虽然这使得它有点困难和参数对象传递给它。

There are to basic approaches View-First and ViewModel First. In View-First you create the View (Page, Window etc) and inject the ViewModel into it (usually via constructor). Though this makes it a bit difficult to and pass a parameter object to it.

这哪里是来的NavigationService。您解决通过IoC容器的视图,然后传递参数给视图模型,也就是说,如果它是一个 UserViewModel 你通过用户id 它和视图模型将加载用户

Which is where the NavigationService comes. You resolve the View via IoC container, then pass a parameter to the ViewModel, i.e. if it's a UserViewModel you'd pass the userId to it and the ViewModel will load the user.

解决办法:导航服务
你可以使用现有的(棱镜或其他MVVM它配备了自己的导航服务框架)。

The solution: Navigation Service You can either use an existing one (Prism, or other MVVM Frameworks which come with their own navigation services).

如果你想有一个自己的简单的,你可以创建一个 INavigationService 接口,并将其注入到你的ViewModels。

If you want a own simple one, you could create an INavigationService interface and inject it into your 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);
}

和实现它就像(我假设你使用IoC容器,因为国际奥委会是一键MVVM加键脱钩的对象使用Unity IoC容器例)

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)

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
{
}

最后你实现你的 PlotViewModel 类,并使用传递的参数加载对象

and finally you implement your PlotViewModel class and use the passed parameter to load the object

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");
            }
        }
    }
}



中当然可以修改的NavigationService 来还可以设置的DataContext 里面。或使用字符串来解决视图/窗口(如棱镜的Windows商店应用程序一样)。

Of course could modify the NavigationService to also set the DataContext inside it. Or use strings to resolve the View/Window (such as Prism for Windows Store Apps does).

而在最后的代码可以通过调用<$ C $打开的窗口C> navigationService.NavigateToWindow< IPlotView>(platId); 在你的代码(即在的ICommand 这势必一个按钮命令属性在XAML。

And in the final code you open the window by calling navigationService.NavigateToWindow<IPlotView>(platId); in your code (i.e. in an ICommand which is bound to a buttons Command Property in your XAML.

这篇关于我怎样才能打开使用点击处理程序和命令在WPF MVVM另一种观点? (是我的解决方案是否合理?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆