原文:MVVMLight 实现指定Frame控件的导航

在UWP开发中,利用汉堡菜单实现导航是常见的方法。汉堡菜单导航一般都需要新建一个Frame控件,并对其进行导航,但是在MvvmLight框架默认的NavigationService中,只能对根Frame进行导航,这就需要我们实现自己的NavigationService了。

MvvmLight源码简析

  • GalaSoft.MvvmLight.Views命名空间下的NavigationService继承了同命名空间下的INavigationService接口但是并没有实现。
namespace GalaSoft.MvvmLight.Views
{
public class NavigationService : INavigationService
{
public const string RootPageKey = "-- ROOT --";
public const string UnknownPageKey = "-- UNKNOWN --";
public NavigationService();
public string CurrentPageKey { get; }
public void Configure(string key, Type pageType);
public void GoBack();
public void NavigateTo(string pageKey);
public virtual void NavigateTo(string pageKey, object parameter);
}
}
  • 具体的功能实现是在GalaSoft.MvvmLight.Platform命名空间下的NavigationService中,根据平台的不同有不同的实现。
  • 我们要做的就是自己实现INavigationService并自己定义导航的Frame控件

实现自己的NavigationService

很奇怪,官方公布源码的那个网站上没有找到Win10平台的Platform源码,所以我就把Win8.1的NavigationService实现复制下来,经测试能正常用。

  • 在public virtual void NavigateTo(string pageKey, object parameter)方法下,我们可以看到这样一行代码:

    var frame = ((Frame)Window.Current.Content);

    这个语句就定义了用来导航的Frame控件(不止这个方法中有,另外2个方法中也有类似的语句)。

  • 我们把这几个地方的frame变量值设置成自己的Frame控件就可以了。
  • 我的修改方法:

    • 我的Frame控件是放在MainPage中的,主要是为了实现汉堡菜单导航,我想大部分有这种需求的人都是为了实现类似的导航吧。

      1. 首先在MainPage.xaml.cs里面加入一个MainPage类型的静态Public变量
      2. 然后再构造函数中给变量赋值为this
      3. 还要给自己的Frame控件写一个属性来获取它

        public static MainPage MPage;   // 1.
        
        public Frame MyFrame            // 3.
        {
        get
        {
        return ContentFrame; //ContentFrame是xaml中定义的Frame控件的名字
        }
        } public MainPage()
        {
        InitializeComponent(); SystemNavigationManager.GetForCurrentView().BackRequested += SystemNavigationManagerBackRequested; Loaded += (s, e) =>
        {
        Vm.RunClock();
        }; MPage = this; // 2.
        }
      4. 然后就可以在自己的NavigationService里面使用自己的Frame了。

        //var frame = ((Frame)Window.Current.Content);    // 之前的语句
        var frame = MainPage.MPage.MyFrame; // 自己写的新语句

        这样我们就把NavigationService的导航Frame设置成了MainPage里面的ContentFrame控件。

  • 不过我这个方法还不是最优的,因为这样写就把MainPage和NavigationService直接联系起来了,增加了耦合性。我看到WPF中可以通过下面这个方法来获取指定名字的Frame控件,不过在UWP里面没有这个方法。

    var frame = GetDescendantFromName(Application.Current.MainWindow, "MainFrame") as Frame;

如果谁有更好的办法的话麻烦在评论里面告诉我,万分感谢。

效果图

MyNavigationService代码

    using GalaSoft.MvvmLight.Views;
using Mvvm_HutHelper.View;
using System;
using System.Collections.Generic;
using System.Linq; namespace Mvvm_HutHelper.Model
{
class MyNavigationService : INavigationService
{
/// <summary>
/// The key that is returned by the <see cref="CurrentPageKey"/> property
/// when the current Page is the root page.
/// </summary>
public const string RootPageKey = "-- ROOT --"; /// <summary>
/// The key that is returned by the <see cref="CurrentPageKey"/> property
/// when the current Page is not found.
/// This can be the case when the navigation wasn't managed by this NavigationService,
/// for example when it is directly triggered in the code behind, and the
/// NavigationService was not configured for this page type.
/// </summary>
public const string UnknownPageKey = "-- UNKNOWN --"; private readonly Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>(); /// <summary>
/// The key corresponding to the currently displayed page.
/// </summary>
public string CurrentPageKey
{
get
{
lock (_pagesByKey)
{
//var frame = ((Frame)Window.Current.Content);
var frame = MainPage.MPage.MyFrame; if (frame.BackStackDepth == 0)
{
return RootPageKey;
} if (frame.Content == null)
{
return UnknownPageKey;
} var currentType = frame.Content.GetType(); if (_pagesByKey.All(p => p.Value != currentType))
{
return UnknownPageKey;
} var item = _pagesByKey.FirstOrDefault(
i => i.Value == currentType); return item.Key;
}
}
} /// <summary>
/// If possible, discards the current page and displays the previous page
/// on the navigation stack.
/// </summary>
public void GoBack()
{
//var frame = ((Frame)Window.Current.Content);
var frame = MainPage.MPage.MyFrame; if (frame.CanGoBack)
{
frame.GoBack();
}
} /// <summary>
/// Displays a new page corresponding to the given key.
/// Make sure to call the <see cref="Configure"/>
/// method first.
/// </summary>
/// <param name="pageKey">The key corresponding to the page
/// that should be displayed.</param>
/// <exception cref="ArgumentException">When this method is called for
/// a key that has not been configured earlier.</exception>
public void NavigateTo(string pageKey)
{
NavigateTo(pageKey, null);
} /// <summary>
/// Displays a new page corresponding to the given key,
/// and passes a parameter to the new page.
/// Make sure to call the <see cref="Configure"/>
/// method first.
/// </summary>
/// <param name="pageKey">The key corresponding to the page
/// that should be displayed.</param>
/// <param name="parameter">The parameter that should be passed
/// to the new page.</param>
/// <exception cref="ArgumentException">When this method is called for
/// a key that has not been configured earlier.</exception>
public virtual void NavigateTo(string pageKey, object parameter)
{
lock (_pagesByKey)
{
if (!_pagesByKey.ContainsKey(pageKey))
{
throw new ArgumentException(
string.Format(
"No such page: {0}. Did you forget to call NavigationService.Configure?",
pageKey),
"pageKey");
} //var frame = ((Frame)Window.Current.Content); // 这句设置导航时用到的Frame控件为根Frame
var frame = MainPage.MPage.MyFrame; frame.Navigate(_pagesByKey[pageKey], parameter);
}
} /// <summary>
/// Adds a key/page pair to the navigation service.
/// </summary>
/// <param name="key">The key that will be used later
/// in the <see cref="NavigateTo(string)"/> or <see cref="NavigateTo(string, object)"/> methods.</param>
/// <param name="pageType">The type of the page corresponding to the key.</param>
public void Configure(string key, Type pageType)
{
lock (_pagesByKey)
{
if (_pagesByKey.ContainsKey(key))
{
throw new ArgumentException("This key is already used: " + key);
} if (_pagesByKey.Any(p => p.Value == pageType))
{
throw new ArgumentException(
"This type is already configured with key " + _pagesByKey.First(p => p.Value == pageType).Key);
} _pagesByKey.Add(
key,
pageType);
}
}
}
}

参考资料

MVVM Light 5.0: How to use the Navigation service

MvvmLight SourceCode

MVVMLight 实现指定Frame控件的导航的更多相关文章

  1. 背水一战 Windows 10 (41) - 控件(导航类): Frame

    [源码下载] 背水一战 Windows 10 (41) - 控件(导航类): Frame 作者:webabcd 介绍背水一战 Windows 10 之 控件(导航类) Frame 示例Controls ...

  2. 背水一战 Windows 10 (42) - 控件(导航类): Frame 动画

    [源码下载] 背水一战 Windows 10 (42) - 控件(导航类): Frame 动画 作者:webabcd 介绍背水一战 Windows 10 之 控件(导航类) Frame 动画 示例An ...

  3. 使用Frame控件设计Silverlight的导航

    这里所说的导航其实就是在Silverlight的页面之间前进后退以及跳转.通过Frame控件配合后台NavigationService类可以很容易的做到页面之间的导航. 这就是工具箱中的Frame控件 ...

  4. 背水一战 Windows 10 (40) - 控件(导航类): AppBar, CommandBar

    [源码下载] 背水一战 Windows 10 (40) - 控件(导航类): AppBar, CommandBar 作者:webabcd 介绍背水一战 Windows 10 之 控件(导航类) App ...

  5. .net验证控件,导航控件

    一.客户端验证(用户体验,减少服务器端压力) 二.服务器端验证(防止恶意攻击,客户端js很容易被绕过) 验证控件:RequiredFieldValidator:字段必填:RangeValidator: ...

  6. <iOS小技巧>UIview指定设置控件圆角

      一.用法:   众所周知,设置控件的圆角使用layer.cornerRadius属性即可,但是这样设置成的结果是4个边角都是圆角类型.   利用班赛尔曲线画角:   //利用班赛尔曲线画角 UIB ...

  7. 【完全开源】百度地图Web service API C#.NET版,带地图显示控件、导航控件、POI查找控件

    目录 概述 功能 如何使用 参考帮助 概述 源代码主要包含三个项目,BMap.NET.BMap.NET.WindowsForm以及BMap.NET.WinformDemo. BMap.NET 对百度地 ...

  8. VC++ 如何在显示对话框的时候,指定焦点控件!

    很简单: 在你的CAddDlg类的OnInitDialog函数中加上你上面的代码GetDlgItem(IDC_EDIT1)->SetFocus();最后记得return FALSE; 其实,不知 ...

  9. C/S模式开发中如何利用WebBrowser控件制作导航窗体

    原文:C/S模式开发中如何利用WebBrowser控件制作导航窗体 转自: CSDN 相信不少同学们都做过MIS系统的开发,今天这里不讨论B/S模式开发的问题.来谈谈winform开发.用过市面上常见 ...

随机推荐

  1. ios开发之手势处理 之手势识别一

    #import "ViewController.h" @interface ViewController ()<UIGestureRecognizerDelegate> ...

  2. PHP 根据对象属性进行对象数组的排序(usort($your_data, "cmp");)(inside the class: usort($your_data, array($this, "cmp")))

    PHP 根据对象属性进行对象数组的排序(usort($your_data, "cmp");)(inside the class: usort($your_data, array($ ...

  3. SetProcessWorkingSetSize() 方法使内存降低了很多(把内存放到交换区,其实会降低性能)——打开后长时间不使用软件,会有很长时间的加载过程,原来是这个!

    在项目中对程序性能优化时,发现用SetProcessWorkingSetSize() 方法使内存降低了很多,于是查阅了相关的资料如下: 我的程序为什么能够将占用的内存移至虚拟内存呢? 其实,你也可以, ...

  4. 算法 Tricks(四)—— 获取一个数二进制形式第一个不为 0 的位置

    int n = ...; int flag = 1; while ((flag & n) == 0) flag <<= 1; // & 运算时,其实判断的是二者的二进制形式 ...

  5. [GeekBand] 设计模式之观察者模式学习笔记

    本文参考文献::GeekBand课堂内容,授课老师:李建忠 :网络资料: http://blog.csdn.net/hguisu/article/details/7556625 本文仅作为自己的学习笔 ...

  6. hive 导出数据的几种方式

    1.使用insert导出 这种方式的优点在于既可以导出到hdfs上还可以导出到本地目录 下面以导出emp表中数据为例 insert overwrite local directory "/o ...

  7. 从vue1迁移到vue2踩到的两个坑

    先说第一个,在vue1中用v-for的时候,习惯性用$index和$key来取键.今天迁移到vue2之前,也知道vue2里不能这样用了,结果还是出问题了, 数据渲染不出来. <li v-for= ...

  8. mongodb 批量更新 数组的键操作的文件

    persons该文件的数据如下面的: > db.persons.find() { "_id" : 2, "name" : 2 } { "_id& ...

  9. vue-router设置页面标题

    通过vue-router设置页面标题 const router = new Router({ routes: [ { path: '/', name: 'EntryConfirmation', met ...

  10. tomcat中同时部署两个项目的问题

    其中一个项目总是无法访问. 参考这两篇文章: 1. tomcat中同时部署两个项目的问题 2. java.lang.IllegalStateException: Web app root system ...