使用MVVM和棱镜在视图之间切换

Sil*_*lva 5 navigation wpf prism view mvvm

我是WPF的新手,但从我所读到的,构建应用程序的正确方法是在同一窗口上切换视图.我的意思是像一个带有菜单的"框架"和一个显示视图的工作区.

到目前为止,我一直在关注这个问题,http://jesseliberty.com/2011/01/06/windows-phone-from-scratch%E2%80%93mvvm-light-toolkit-soup-to-nuts-3/ 但是这是为WP7而我不能在WPF应用程序上使用NavigationService.

我可以说,我想要的最简单的事情是,mainwindow.xaml上面有一个显示按钮的视图,当我按下那个按钮时我希望在同一个窗口上显示一个新视图(并且旧视图消失) .

实现类似的东西的正确方法是什么?

编辑:这开始使用mvvm-light,但最终演变为棱镜.请参阅我的上一个答案以获取更多详

J T*_*ana 2

这是一个很好的问题 - 当我开始使用 MVVM 时,我也遇到过类似的问题。我只使用 MS 的 Prism/CAL 库。

我相信您正在寻找的是 CAL 中的区域的想法。它基本上是一个呈现事物的命名容器控件。基本上,您可以在顶级 UI 部分中命名一个区域,例如主窗口。您的应用程序可能有一小部分:可能是页眉、页脚和主窗口区域。(无论如何,我就是这样做的。)。然后,您可以从后面的代码中通过区域管理器访问该区域,清除它,然后放入 ViewModel。ViewModel 被映射到适当的 View,瞧,新的 View 出现了。

从编码的角度来看,我通常看到它是这样分解的:您有某种 NavigationController,它具有一两个用于清除区域并显示新 ViewModel->View 的方法,并且还具有诸如 GoToPageX() 之类的方法。这抽象了区域经理。然后每个页面都有一个 ViewModel 和一个 View。每个 ViewModel 通过依赖注入接收 NavigationController(但如果您不使用 DI,则可以创建新的)。然后在 ViewModel 上,它公开一个映射到按钮并调用 NavigationController 的 Command。

在某个地方,您还必须将 ViewModel 与用于显示它们的视图一起注册。

这是一个导航控制器的示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Unity;
using KSheets.CoreModule.Presenter;
using Zephyr.Core.Logging;
using KSheets.CoreSheets.Sheet;
using System.IO;

namespace KSheets.CoreModule.Switch
{
    public class Switchboard : ISwitchboard
    {
        private ILoggingService<ISwitchboard> m_Logger;
        private IRegionManager m_RegionManager;
        private IUnityContainer m_UnityContainer;

        public Switchboard(
            ILoggingService<ISwitchboard> loggingService,
            IRegionManager regionManager,
            IUnityContainer unityContainer
            )
        {
            if (loggingService == null) throw new ArgumentNullException("loggingService");
            if (regionManager == null) throw new ArgumentNullException("regionManager");
            if (unityContainer == null) throw new ArgumentNullException("unityContainer");

            m_RegionManager = regionManager;
            m_UnityContainer = unityContainer;
            m_Logger = loggingService;
        }

        public void GoHome()
        {
            m_Logger.Log("Going home");

            var worksheetEditor = m_UnityContainer.Resolve<IWorksheetEditor>();
            worksheetEditor.Initialize();
            LoadView(RegionNames.EditorRegion, worksheetEditor);

            var batchExporter = m_UnityContainer.Resolve<IExportBatchPresenter>();
            LoadView(RegionNames.ExporterRegion, batchExporter);
        }

        private void LoadView(string regionName, object newView)
        {
            var region = m_RegionManager.Regions[regionName]; 
            var oldViews = region.Views;
            foreach (var oldView in oldViews)
                region.Remove(oldView);
            region.Add(newView);
            region.Activate(newView);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是通过编程方式向 ViewModel 注册视图的示例。许多人在 XAML 中执行此操作,但您也可以在代码中执行此操作,如果您使用依赖项注入,IMO 效果会更好,因为您可以在模块加载时同时注册视图和依赖项注入内容。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace Zephyr.WPF.Utils
{
    public static class ResourceUtils
    {
        public static void RegisterView<T, U>()
        {
            DataTemplate template = new DataTemplate();
            FrameworkElementFactory factory = new FrameworkElementFactory(typeof(U));
            template.DataType = typeof(T).FullName;
            template.VisualTree = factory;
            Application.Current.Resources.Add(new DataTemplateKey(typeof(T)), template);
        }
    }
}

        private void RegisterViews()
        {
            ResourceUtils.RegisterView<WorksheetDisplay, WorksheetDisplayView>();
            ResourceUtils.RegisterView<ProblemSelector, ProblemSelectorView>();
            ResourceUtils.RegisterView<WorksheetEditor, WorksheetEditorView>();
            ResourceUtils.RegisterView<ExportBatchPresenter, ExportBatchView>();
        }
Run Code Online (Sandbox Code Playgroud)

归根结底,您需要一小段 UI 感知代码(或者一个 UI 控件,用于侦听从非 UI 代码发送的消息,该代码了解一些关于UI 的信息,如区域名称)并允许您粘合ViewModel 就位。但是,此代码通常非常少,并且除了开箱即用的组件之外,绝对不需要隐藏代码。当您第一次在 WPF 中实际实现 MVVM 时,需要了解很多内容;启动并运行一个简单的应用程序需要一个陡峭的学习曲线。