前言

在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. 京东二面:Sychronized的锁升级过程是怎样的

    引言 Java作为主流的面向对象编程语言,提供了丰富的并发工具来帮助开发者解决多线程环境下的数据一致性问题.其中,内置的关键字"Synchronized"扮演了至关重要的角色,它能 ...

  2. 《最新出炉》系列入门篇-Python+Playwright自动化测试-47-自动滚动到元素出现的位置

    1.简介 在我们日常工作中或者生活中,经常会遇到我们的页面内容较多,一个屏幕范围无法完整展示内容,我们就需要滚动滚动条去到我们想要的地方,如下图页面,我们虽然在豆瓣首页,但是内容并不完整,如果我们想要 ...

  3. C# java 的 equals 与 == 的区别

    C# String a = "宜春"; String b = new String(new char[] { '宜', '春' }); String c = b; //注意这里是引 ...

  4. 浅析MySQL 8.0直方图原理

    本文分享自华为云社区<[MySQL技术专栏]MySQL8.0直方图介绍>,作者:GaussDB 数据库. 背景 数据库查询优化器负责将SQL查询转换为尽可能高效的执行计划,但因为数据环境不 ...

  5. go append的坑

    b := []int{1,2,3,4,5} slice := b[:2] newSlice := append(slice, 50) fmt.Println(b) fmt.Println(newSli ...

  6. ceph deploy部署ceph集群 ceph扩容 rbd存储

    架构拓扑 节点主机 节点IP 节点角色 OS ceph-admin 10.0.0.60 admin deploy mds centos7 ceph-node1 10.0.0.61 mon osd md ...

  7. rhce练习题容易错的地方

    rhce练习题里容易错的地方 使用导航器的时候,ssh连接 因为导航器是一个工具,生成一个容器,在容器里面运行playbook 安装软件包的时候,多个软件包使用循环loop loop的格式 - hos ...

  8. Redis 常用的数据结构简介与实例测试【Redis 系列二】

    〇.都有哪些数据结构? Redis 提供了较为丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合).Zset(有序集合). 随着 Redis 版本的 ...

  9. 01-布局扩展-BFC完成圣杯布局

    <!DOCTYPE html>   <html lang="en">   <head>   <meta charset="UTF ...

  10. mysql数据库慢SQL优化

    mysql数据库慢SQL优化优化来源: 阿里云 云数据库RDS 慢sql 或者CAT监控系统中的Transaction SQL or URL根据平均时间反馈来排查,决定是否增加索引,或者调整业务逻辑代 ...