WPF/C#:实现导航功能
前言
在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#:实现导航功能的更多相关文章
- WPF ScrollViewer(滚动条) 自定义样式表制作 图文并茂
原文:WPF ScrollViewer(滚动条) 自定义样式表制作 图文并茂 先上效果图 正常样式 拖动时样式 好下面 开始吧 ==================================== ...
- 在WPF中使用依赖注入的方式创建视图
在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...
- MVVM框架从WPF移植到UWP遇到的问题和解决方法
MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...
- MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息
MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...
- MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信
MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...
- MVVM设计模式和WPF中的实现(四)事件绑定
MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式解析和在WPF中的实现(三)命令绑定
MVVM模式解析和在WPF中的实现(三) 命令绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式和在WPF中的实现(二)数据绑定
MVVM模式解析和在WPF中的实现(二) 数据绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式和在WPF中的实现(一)MVVM模式简介
MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...
- 逆天通用水印支持Winform,WPF,Web,WP,Win10。支持位置选择(9个位置 ==》[X])
常用技能:http://www.cnblogs.com/dunitian/p/4822808.html#skill 逆天博客:http://dnt.dkil.net 逆天通用水印扩展篇~新增剪贴板系列 ...
随机推荐
- 鸿蒙HarmonyOS实战-Stage模型(应用上下文Context)
前言 应用上下文(Context)是应用程序的全局信息的接口.它是一个抽象类,提供了访问应用程序环境的方法和资源的方法.应用上下文可以用于获取应用程序的资源.启动Activity.发送广播等.每个应用 ...
- 手写Word2vec算法实现
1. 语料下载:https://dumps.wikimedia.org/zhwiki/latest/zhwiki-latest-pages-articles.xml.bz2 [中文维基百科语料] 2. ...
- .NetCore 3.1 教程之 EFCore连接Mysql DBFirst模式 从数据库生成实体
一:创建EF的类库,同时将此项目设置为启动项(为Scaffold-DbContext -tables指令使用),同时安装2个包 ①Microsoft.EntityFrameworkCore.Too ...
- Vue 页面传参方式 Query 和 Params
1. query 与 params 传参 query 需要和配合 path 属性使用,携带参数会拼接在请求路径后,效果同 Get 请求方式 http://localhost:8033/Permissi ...
- 理解太阳辐射 DNI DHI GHI
理解太阳辐射 DNI DHI GHI DNI: Direct Normal Irradiance 阳光从太阳盘面直接照射到与光路正交的表面,称作直接辐射简写为 DNI. DHI: Diffuse ...
- centos7下利用qemu搭建arm模拟器
1 目的 在centos7下使用qemu搭建arm模拟器. 参考博客: centos下利用qemu搭建arm模拟器 - 寒水司天 - 博客园 (cnblogs.com) 从零使用qemu模拟器搭建ar ...
- NFS练习
NFS练习题 1.开放/nfs/share目录,提供给 任意用户 只读(/etc/exports ro) 查询 1.任意客户端2.任意的用户 服务端 showmout exportfs system ...
- 网站_域名_DNS_端口_web访问过程
网站基本概念 服务器:能够提供服务器的机器,取决于机器上所安装的服务软件 web服务器:提供web服务(网站访问),需要安装web服务软件,Apache,tomcat,iis等 域名 (Domain ...
- tortoisegit 配置ssh登录
习惯使用小乌龟. 服务器:gitblit 客户端:TortoiseGit 2.13.0.1 (C:\Program Files\TortoiseGit\bin) git version 2.36.1. ...
- itunes同步视频
itunes同步视频不要通过影片同步,而是通过照片同步 如果显示的是iCloud,只需在手机或ipad上,设置里icloud中,包照片的iCloud关掉,然后重连一下就行