前言

在WPF中使用导航功能可以使用Frame控件,这是比较基础的一种方法。前几天分享了wpfui中NavigationView的基本用法,但是如果真正在项目中使用起来,基础的用法是无法满足的。今天通过wpfui中的mvvm例子来说明在wpfui中如何通过依赖注入与MVVM模式使用导航功能。实践起来,我个人觉得这个例子中实现导航功能还是有点麻烦的,但我也不知道怎么能更优雅,也是学到了一些东西吧。

wpfui中MVVM例子的地址在:https://github.com/lepoco/wpfui/tree/main/src/Wpf.Ui.Demo.Mvvm

实现效果如下所示:

如果你对此感兴趣,可以继续阅读。

实践

使用依赖注入

将主窗体与主窗体的ViewModel与每个页面与每个页面的ViewModel都存入依赖注入容器中:

当然不只是窗体页面与ViewModel,也需要注册一些服务。

为了实现导航功能,使用了两个服务分别是NavigationService与PageService。

NavigationService在wpfui库中已经自带了,直接使用即可:

具体代码可自行研究,这里就不放了。

而PageService在wpfui中没有自带,需要自己定义,MVVM例子中的定义如下所示:

 public class PageService : IPageService
{
/// <summary>
/// Service which provides the instances of pages.
/// </summary>
private readonly IServiceProvider _serviceProvider; /// <summary>
/// Initializes a new instance of the <see cref="PageService"/> class and attaches the <see cref="IServiceProvider"/>.
/// </summary>
public PageService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
} /// <inheritdoc />
public T? GetPage<T>()
where T : class
{
if (!typeof(FrameworkElement).IsAssignableFrom(typeof(T)))
{
throw new InvalidOperationException("The page should be a WPF control.");
} return (T?)_serviceProvider.GetService(typeof(T));
} /// <inheritdoc />
public FrameworkElement? GetPage(Type pageType)
{
if (!typeof(FrameworkElement).IsAssignableFrom(pageType))
{
throw new InvalidOperationException("The page should be a WPF control.");
} return _serviceProvider.GetService(pageType) as FrameworkElement;
}
}

现在已经将所有窗体、页面、ViewModels与相关服务都注册到容器中了。

ViewModel

在MainWindowViewModel中将页面存入一个属性中:

在非首页的ViewModel中实现INavigationAware接口:

View

MainWindow.cs如下所示:

 public partial class MainWindow : INavigationWindow
{
public ViewModels.MainWindowViewModel ViewModel { get; } public MainWindow(
ViewModels.MainWindowViewModel viewModel,
IPageService pageService,
INavigationService navigationService
)
{
ViewModel = viewModel;
DataContext = this; Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this); InitializeComponent();
SetPageService(pageService); navigationService.SetNavigationControl(RootNavigation);
} public INavigationView GetNavigation() => RootNavigation; public bool Navigate(Type pageType) => RootNavigation.Navigate(pageType); public void SetPageService(IPageService pageService) => RootNavigation.SetPageService(pageService); public void ShowWindow() => Show(); public void CloseWindow() => Close(); /// <summary>
/// Raises the closed event.
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e); // Make sure that closing this window will begin the process of closing the application.
Application.Current.Shutdown();
} INavigationView INavigationWindow.GetNavigation()
{
throw new NotImplementedException();
} public void SetServiceProvider(IServiceProvider serviceProvider)
{
throw new NotImplementedException();
}
}

首先实现了INavigationWindow接口。在构造函数中注入所需的依赖类。注意这里的RootNavigation其实就是页面中NavigationView的名称:

刚开始看这里没注意到,卡壳了很久。

因为你在代码中查看定义,它会转到这个地方:

没经验不知道是什么,但是这次过后,知道这是在Xaml中定义,由工具自动生成的代码了。

其他的页面改成了这样的写法:

 public partial class DashboardPage : INavigableView<DashboardViewModel>
{
public DashboardViewModel ViewModel { get; }
public DashboardPage(DashboardViewModel viewModel)
{
ViewModel = viewModel;
this.DataContext = this;
InitializeComponent();
}
}

都实现了INavigableView<out T>接口:

显示主窗体与主页面

现在准备工作都做好了,下一步就是显示主窗体与主页面了。

在容器中我们也注入了这个:

ApplicationHostService如下所示:

    /// <summary>
/// Managed host of the application.
/// </summary>
public class ApplicationHostService : IHostedService
{
private readonly IServiceProvider _serviceProvider;
private INavigationWindow? _navigationWindow; public ApplicationHostService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
} /// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
public async Task StartAsync(CancellationToken cancellationToken)
{
await HandleActivationAsync();
} /// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
public async Task StopAsync(CancellationToken cancellationToken)
{
await Task.CompletedTask;
} /// <summary>
/// Creates main window during activation.
/// </summary>
private async Task HandleActivationAsync()
{
await Task.CompletedTask; if (!System.Windows.Application.Current.Windows.OfType<MainWindow>().Any())
{
_navigationWindow = (
_serviceProvider.GetService(typeof(INavigationWindow)) as INavigationWindow
)!;
_navigationWindow!.ShowWindow(); _ = _navigationWindow.Navigate(typeof(DashboardPage));
} await Task.CompletedTask;
}
}
}

在app.xaml中定义了程序启动与退出事件的处理程序:

 /// <summary>
/// Occurs when the application is loading.
/// </summary>
private async void OnStartup(object sender, StartupEventArgs e)
{
await _host.StartAsync();
} /// <summary>
/// Occurs when the application is closing.
/// </summary>
private async void OnExit(object sender, ExitEventArgs e)
{
await _host.StopAsync(); _host.Dispose();
}

整个过程回顾

在OnStartup方法中打个断点,理解这个过程:

点击下一步:

到ApplicationHostService中了,一步一步调试,注意这个地方:

因为主窗体实现了INavigationWindow接口,这里获取了主窗体并将主窗体显示,然后调用主窗体中的Navigate方法,导航到DashPage页面,之后点继续,结果如下所示:

最后

以上就是自己最近学习wpfui中导航功能实现的笔记,在自己的项目中也成功使用,对于可能会经常修改代码增加功能的程序这样做感觉挺好的,但是如果你只是使用WPF做一个简单的小工具,感觉这样做增加了复杂度,不用依赖注入,不用做这么复杂的导航,甚至不使用MVVM模式都可以。

WPF/C#:实现导航功能的更多相关文章

  1. WPF ScrollViewer(滚动条) 自定义样式表制作 图文并茂

    原文:WPF ScrollViewer(滚动条) 自定义样式表制作 图文并茂 先上效果图 正常样式 拖动时样式 好下面 开始吧 ==================================== ...

  2. 在WPF中使用依赖注入的方式创建视图

    在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...

  3. MVVM框架从WPF移植到UWP遇到的问题和解决方法

    MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...

  4. MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息

    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...

  5. MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信

    MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...

  6. MVVM设计模式和WPF中的实现(四)事件绑定

    MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  7. MVVM模式解析和在WPF中的实现(三)命令绑定

    MVVM模式解析和在WPF中的实现(三) 命令绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  8. MVVM模式和在WPF中的实现(二)数据绑定

    MVVM模式解析和在WPF中的实现(二) 数据绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  9. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  10. 逆天通用水印支持Winform,WPF,Web,WP,Win10。支持位置选择(9个位置 ==》[X])

    常用技能:http://www.cnblogs.com/dunitian/p/4822808.html#skill 逆天博客:http://dnt.dkil.net 逆天通用水印扩展篇~新增剪贴板系列 ...

随机推荐

  1. 分布式任务调度内的 MySQL 分页查询优化

    作者:vivo 互联网数据库团队- Qiu Xinbo 本文主要通过图示介绍了用主键进行分片查询的过程,介绍了主键分页查询存在SQL性能问题,如何去创建高效的索引去优化主键分页查询的SQL性能问题.对 ...

  2. 胃食管反流之 SAP分析( in the Ohmega software)

    原文:https://note.youdao.com/s/GED6wise SAP analysis in the Ohmega software ohmega software 关于胃食管反流疾病 ...

  3. 记录一次 对应用程序日志排查,老是刷出有本地ip登录Sqlserver数据库失败的日志

    在我电脑-计算机管理-事件查看器-windows日志-应用程序里 1秒中就刷很多条 用户sa登录某个数据库失败,客户端ip:192....; 我查看ip发现是本机的ip地址.也就是说有本地的应用程序在 ...

  4. 关于Zotero组件在office word中的添加

    引言:学姐问我,怎么弄Zotero,在调试过程中遇到的问题及解决方案 Q&A Zotero的安装 找到官网(https://www.zotero.org/)点击download,安装(个人觉得 ...

  5. iOS线程While-True死循环会发生什么

    一.在工作的代码有一段while-True轮训的逻辑,循环中主要的工作是阻塞的IO 代码大概如下: dispatch_async(dispatch_get_global_queue(0, 0), ^{ ...

  6. BLP 模型

    公号:Rand_cs BLP 模型 本篇文章是调研了许多资料后对 BLP 模型的一个总结 MLS,Multi-level Security,主要关心的是数据机密性 D. Elliott Bell 和 ...

  7. C# DateTime日期字段转中文文字

    public static String ToChineseYearAndMonth(this DateTime dt) { string[] chineseNumbers = { "零&q ...

  8. MoneyPrinterPlus:AI自动短视频生成工具-腾讯云配置详解

    MoneyPrinterPlus可以使用大模型自动生成短视频,其中的语音合成和语音识别部分需要借助于一些第三发云厂商的语音服务. 很多小伙伴可能不知道应该如何配置,这里给大家提供一个详细的腾讯云语音服 ...

  9. 实验13.Nat转发telnet实验

    # 实验13.Nat转发telnet实验 本节用于测试NAT服务,用于将流量转发到内网的指定设备上. 实验组 配置路由器 由于之前配置过ospf,所以这次用直接指静态练手,首先确保全网畅通 R3 GW ...

  10. 算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全

    大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 决策树是一种简单直观的机器学习算法,它广泛应用于分类和回归问题中.它的核心思想是将复杂 ...