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

标签 c# wpf mvvm event-handling viewmodel

我正在编写一个有两个窗口的 WPF 应用程序。

我有一个 MainWindowViewModel包含另外两个 View 模型:AllTagsViewModelPlotViewModel .

public AllTagsViewModel AllTagsViewModel { get; private set; }

public PlotViewModel PlotViewModel { get; private set; }

目前,我将此解决方案用作主窗口中的单击处理程序:
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();
        }
    }

我还将命令绑定(bind)到按钮。命令在MainWindowViewModel它使用构造函数 PlotViewModel(AllTagsViewModel atvm) 实例化一个新的 PlotViewModel .

问题在于设置数据上下文的命令在单击处理程序之后执行。这意味着 PlotWindow第二次打开时按预期工作。

这个问题有什么更好的解决方案?我可以使用事件来保留 AllTagsViewModelPlotViewModel始终与 MainWindowViewModel 中的那个保持同步?我目前的解决方案感觉就像是一个黑客和非常糟糕的做法。

感谢您的建议。

最佳答案

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

有 View-First 和 ViewModel First 的基本方法。在 View-First 中,您创建 View (页面、窗口等)并将 ViewModel 注入(inject)其中(通常通过构造函数)。虽然这使得向它传递参数对象有点困难。

这就是 NavigationService 的来源。您通过 IoC 容器解析 View ,然后将参数传递给 ViewModel,即如果它是 UserViewModel你会通过 userId到它,ViewModel 将加载用户。

解决方案:导航服务
您可以使用现有的(Prism 或其他带有自己的导航服务的 MVVM 框架)。

如果你想要一个自己的简单的,你可以创建一个 INavigationService接口(interface)并将其注入(inject)到您的 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 容器,因为 IoC 是 MVVM 的关键,用于键解耦对象。Unity IoC 容器的示例)
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类并使用传递的参数加载对象
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在里面。或者使用字符串来解析 View /窗口(例如 Prism for Windows Store Apps)。

在最终代码中,您可以通过调用 navigationService.NavigateToWindow<IPlotView>(platId); 打开窗口在您的代码中(即在 ICommand 中,它绑定(bind)到 XAML 中的按钮 Command 属性。

关于c# - 如何使用单击处理程序和命令在 WPF MVVM 中打开另一个 View ? (我的解决方案合理吗?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28817827/

相关文章:

wpf - 如何设置 Avalon 对接管理器来像 VS 一样调整大小?

wpf - 跨 View 模型的命令

c# - 如何检查滚动条当前在 WPF DataGrid 中是否可见?

Silverlight Combobox - 设置 SelectedItem MVVM

c# - 如何从 SQL Azure 应用服务 Web 应用写入日志文件?

c# - Process.Start - 将 html 代码作为参数传递给 exe

c# - 如何将 DataGridTemplateColumn 的 Visibility 绑定(bind)到 textBlock 的 Visibility

WPF MVVM 路由事件

c# - 共享点 2010 : How to extract personalized web part property for all users

c# - 无法在javascript中通过id获取值文本框