开发者

Changing between views using MVVM with prism

开发者 https://www.devze.com 2023-02-28 13:53 出处:网络
I\'m new to WPF, but from what I\'ve read, a correct way to build applications is to switch up views on the same window.

I'm new to WPF, but from what I've read, a correct way to build applications is to switch up views on the same window. What I mean is something like a "frame" with a menu and a workspace where views are shown.

So far I've been following this, http://jesseliberty.com/2011/01/06/windows-phone-from-scratch%E2%80%93mvvm-light-toolkit-soup-to-nuts-3/ but that is for WP7 and I can't use the NavigationService on a WPF app.

I could say that the simplest thing I want is, the mainwindow.xaml has a view on it that displays a button, when I press that button I want a new view to be displayed on the same window (and the old view to disappear).

What is the correct way of implementing something like that?

EDIT: This started by using mvvm-light, but eventually evolved to prism. See my last answer to further det开发者_开发知识库ails.


This is a great question - I had similar questions when I started using MVVM. I use just the Prism/CAL libraries from MS.

I believe what you're looking for is the idea of a Region in CAL. It's basically a named container control that presents things. Basically, you name a region in a top-level UI piece, like your main window. Your app probably has a small number of these: maybe a header, footer, and main window regions. (That's how I've done it anyways.). Then from code behind you can access the region through a region manager, clear it, and drop in your ViewModel. The ViewModel gets mapped to it's appropriate View and voila the new View appears.

From a coding standpoint, I've usually seen it broken up like this: You have some sort of a NavigationController that has a method or two from clearing the region and showing a new ViewModel->View and also has methods like GoToPageX(). This abstracts the region manager. Then you've got a ViewModel for each page and a View for each page. Each ViewModel takes in the NavigationController via dependency injection (but you can create new ones if you're not using DI). Then on the ViewModel, it exposes a Command which gets mapped to the button and calls the NavigationController.

Somewhere you've also got to register the ViewModels with the Views used to show them.

Here's an example of a NavigationController:

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

Here's an example of registering a View with a ViewModel programmatically. Many folks do this in XAML but you can also do it in code which IMO works better if you're using dependency injection as you can register your views and your dependency injection stuff all at the same time when your module loads.

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

At the end of the day, you need a small bit of code that is UI aware (or a UI control that listens to messages sent from non-UI code that knows a small bit about the UI like region names) and allows you to glue the ViewModel into place. However, this code is usually super minimal and definitely requires no code-behind except for out of box components. MVVM is kind of a lot to take in when you actually have to implement it in WPF the first time; a steep learning curve for getting a simple app up and running.


I came here to complement the answer by J-Trana.

He really got me curious for the Prism and I'm glad he did. This just rocks. I've spent the last few weeks reading about it, watching the docs and the quickstart examples that come with it. These were my main references.

So, about the solution...

Prism Regions was indeed what I wanted. The Switchboard provided by him was key in my implementation but I tweaked it a little.

First I passed the UnityContainer inside it, so I could get more flexibility. (Or so I think it gives me.) Second, I've created a method LoadModule (because I have a modular structure) like this.

public void LoadModule(string module) { 
            IModuleManager moduleManager = m_UnityContainer.Resolve(); 

            moduleManager.LoadModule(module); 
}

From what I got, when this LoadModule executes the Initialize() method of a my modules, which are something like this...

public void Initialize()
    {

        Switchboard switchboard = Switchboard.GetSwitchboard();

        IUnityContainer container = switchboard.GetCatalog();

        switchboard.LoadView(RegionNames.ShellMainRegion, container.Resolve<HelloWorldView>());

    }

And this works!

Some tips worth of note: - `//The property that provides context(ViewModel) to the view.

    [Dependency]
    public HelloWorldViewModel HelloWorldViewModel
    {
        set
        {
            this.DataContext = value;
        }
    }
  • That connects a ViewModel to a View as a property on the Views' code behind.
  • this.Container.RegisterType();
  • I register my modules this way:

` Type HelloWorldType = typeof(HelloWorldModule);
this.ModuleCatalog.AddModule(new ModuleInfo()

        {

            ModuleName = ModuleNames.HelloWorldModule,

            ModuleType = HelloWorldType.AssemblyQualifiedName,

            InitializationMode = InitializationMode.OnDemand

        }); `

- And my switchboard is on my infrastructure module. The View creation is shoot up to the module that has it and this solved my problem.

I hope this isn't too confusing, and I really would like some comments on how I'm doing this stuff... Am I doing this the "right" way? Is there a more elegant way? Stuff Like that...

Anyway, I hope this helps and motivates people that are trying to learn prism.

PS: How does this code tag work? And can I change the tags? mvvm-light makes no sense anymore.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号