Windows Phone开发:「优雅」地将NavigationHelper和Frame对象传入ViewModel中

栏目: 前端 · 发布时间: 6年前

内容简介:Windows Phone开发:「优雅」地将NavigationHelper和Frame对象传入ViewModel中

最近因为项目需要接触了Windows Phone 8.1的开发,熟悉了基本的概念后,发现微软默认给了几个Common的类帮助构建应用,分别是NavigationHelper用于帮助页面之间的导航,然后其通过和SuspensionManager这个类来实现页面状态的保存和恢复。当然SuspensionManager类给出的默认序列化器效率并不是很好,但是很容易定制自己的解决方案,在此不再多言。

因之前做过WPF和Silverlight的开发,所以打算继续采用MVVM模型来进行开发。根据习惯,采用MVVMLight库。该库轻量而且相关组件都比较直接,可定制性很强。然后开始尝试开发,主要遇到了问题:

  1. 页面切换一般通过Frame来进行,如果涉及到页面切换的逻辑,就需要在ViewModel能拿到该页面对应的Frame对象。
  2. 每个页面的状态保存的时机一般通过监听NavigationHelper的LoadState和SaveState事件来完成,就MVVM而言,也就是需要将ViewModel的状态序列化或反序列化。NavigationHelper一般在Page初始化时构造,然后传入该Page对象,因为在其构造函数中,要通过监听Page对象的几个事件来完成其功能。也就是说,如果要想在ViewModel中处理页面状态的序列化和反序列化工作,也就需要能拿到每个页面对应的NavigationHelper对象。

然后我想达成的是:

  1. 通过一种通用的形式来「优雅」将Frame对象和NavigationHelper对象传入ViewModel中,而不需要将一部分逻辑写在页面的.cs文件中。
  2. ViewModel模型的绑定在xaml中完成,力求清晰直观。
  3. 尽量沿用标准的NavigationHelper和SuspensionManager提供的模式。

然后经过思考,我自己给出了一个解决方案,基本上解决了这个问题,自认为效果还不错,所以写出来分享下。

具体思路就是借助控制反转(IoC)和附加属性(Attached Property)来完成ViewModel的绑定。根据习惯,我采用的IoC框架是Autofac。

首先是ViewLocator的代码,熟悉MVVMLight框架的应该比较明白,这个类的默认实现就是将ViewModel注册在这里,然后通过属性绑定到具体的页面中去。这里我们做一些修改,加入附加属性:

	public class ViewModelLocator
	{
		private readonly IContainer _container;  // Autofac Container

		public ViewModelLocator()
		{
			var builder = new ContainerBuilder();
			builder.RegisterType<TestMainModel>().Keyed<ExtendViewModelBase>("TestMain");   // 注册ViewModel
this._container = builder.Build();
		}
		public HiwedoViewModelBase GetValueModel(string key)
		{
			if (!this._container.IsRegisteredWithKey<HiwedoViewModelBase>(key))
			{
				throw new Exception("Unregistered viewmodel: " + key);
			}
			return this._container.ResolveKeyed<HiwedoViewModelBase>(key);
		}
		public static readonly DependencyProperty ViewModelKeyProperty = DependencyProperty.RegisterAttached(
			"ViewModelKey",
			typeof(string),
			typeof(ViewModelLocator),
			new PropertyMetadata(default(string))
			);
		public static void SetViewModelKey(ExtendPage page, string value)
		{
			page.SetValue(ViewModelKeyProperty, value);
			SetViewModelForPage(page, value);
		}
		public static string GetViewModelKey(ExtendPage page)
		{
			return (string)page.GetValue(ViewModelKeyProperty);
		}
		public static void SetViewModelForPage(ExtendPage page, string key)
		{
			var locator = YourServiceLocator.Current.Resolve<ViewModelLocator>();  // 通过全局的ServiceLocator获取单例的ViewModelLocator
			var viewModel = locator.GetValueModel(key);
			viewModel.SetNavigationHelper(page.NavigationHelper);	 // 将Page的NavigationHelper传入到ViewModel中
			page.DataContext = viewModel;   // 将ViewModel传入到Page的DataContext中
		}
		public static void Cleanup()
		{
		}
	}

需要注意的是,在SetViewModelForPage方法中,我是通过全局的ServiceLocator来获取单例模式的ViwModelLocator的,当然这里也可以有别的方案,根据自己的需要定制即可。当然在我这种方案中,ViewModelLocator需要在App的OnLaunched()事件中完成初始化。

同时对于传入的Page会发现我并没有直接使用Page类,而是使用了一个ExtendPage类,这个类是我对于Page类的继承类,下面将会列出,这样做的原因是因为需要在该方法中拿到NavigationHelper,为此拓展了Page类,具体实现如下:

    public class ExtendPage : Page
    {
	private readonly NavigationHelper _navigationHelper;
	public ExtendPage()
	{
	    this._navigationHelper = new NavigationHelper(this);
	}
	public NavigationHelper NavigationHelper
	{
	    get { return this._navigationHelper; }
	}
	#region NavigationHelper registration
	protected override void OnNavigatedTo(NavigationEventArgs e)
	{
	    this._navigationHelper.OnNavigatedTo(e);
	}
	protected override void OnNavigatedFrom(NavigationEventArgs e)
	{
	    this._navigationHelper.OnNavigatedFrom(e);
	}
	#endregion
    }

这个类的代码比较简单,基本就是微软给出的示例代码。只不过我增加了一个NavigationHelper的公开属性,具体原因已经在上面提到过了。

这样之后,在页面中绑定ViewModel就可以通过如下代码进行了:

<common:ExtendPage
	x:Class="Hiwedo.WindowsPhone.Views.TestMain"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="using:Hiwedo.WindowsPhone.Views"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:common="using:Hiwedo.WindowsPhone.Common"
	xmlns:viewModel="using:Hiwedo.WindowsPhone.ViewModels"
	mc:Ignorable="d"
	Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
	xmlns:i="using:Microsoft.Xaml.Interactivity"
	xmlns:core="using:Microsoft.Xaml.Interactions.Core"
	viewModel:ViewModelLocator.ViewModelKey="TestMain">

最后一行即是通过依赖属性实现DataContext绑定的代码,这里的ViewModelKey应该和在ViewModelLocator注册ViewModel时用到的Key保持一致。同时还要注意,这个Page的类型已经修改成了ExtendPage,不光需要在xaml中修改,同时还要将对应的.cs文件的类型修改成ExtendPage。

最后,需要看一下拓展的ViewModelBase类,也就是将ViewModelBase类做了拓展。

public class ExtendViewModelBase : ViewModelBase
    {
	private NavigationHelper _navigationHelper;
	public  ExtendViewModelBase()
	{
	}
	public HiwedoViewModelBase(NavigationHelper navigationHelper)
	{
	    _navigationHelper = navigationHelper;
	}
	public void SetNavigationHelper(NavigationHelper navigationHelper)
	{
	    this._navigationHelper = navigationHelper;
	    this._navigationHelper.LoadState += NavigationHelper_LoadState;
	    this._navigationHelper.SaveState += NavigationHelper_SaveState;
	}
	private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
	{
	    await LoadStateAsync(sender, e);
	}
	private async void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
	{
	    await SaveStateAsync(sender, e);
	}
	protected virtual async Task LoadStateAsync(object sender, LoadStateEventArgs e) { }
	protected virtual async Task SaveStateAsync(object sender, SaveStateEventArgs e) { }
	protected NavigationHelper NavigationHelper
	{
	    get { return _navigationHelper; }
	    set { _navigationHelper = value; }
	}
    }

具体写ViewModel的时候,只需要继承ExtendViewModelBase,然后重载LoadStateAsync和SaveStateAsync方法来实现相关状态的加载和保存即可。然后Frame的获取可以通过NavigationHelper中存有的Page对象得到。

以上就是我的解决方案,基本上解决了我提出的几个问题,然后这个解决方案现有的一个问题就是,在开发过程中,因为具体ViewModel的类型并不清楚,所以无法在编写XAML的绑定语句时实现智能提示,不过有一种解决思路就是通过在设计时,通过d:DataContext='xxx'来制定一个设计时用的ViewModel。

当然如果您有更好的方案也欢迎提出来。欢迎指正。


以上所述就是小编给大家介绍的《Windows Phone开发:「优雅」地将NavigationHelper和Frame对象传入ViewModel中》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Blog Design Solutions

Blog Design Solutions

Richard Rutter、Andy Budd、Simon Collison、Chris J Davis、Michael Heilemann、Phil Sherry、David Powers、John Oxton / friendsofED / 2006-2-16 / USD 39.99

Blogging has moved rapidly from being a craze to become a core feature of the Internetfrom individuals sharing their thoughts with the world via online diaries, through fans talking about their favori......一起来看看 《Blog Design Solutions》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具