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都存入依赖注入容器中:

image-20240718141334286

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

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

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

image-20240718141645305

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

而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中将页面存入一个属性中:

image-20240718142334814

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

image-20240718142456377

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的名称:

image-20240718142925133

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

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

image-20240718143106472

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

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

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

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

image-20240718143558501

显示主窗体与主页面

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

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

image-20240718144029024

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中定义了程序启动与退出事件的处理程序:

image-20240718144223862

 /// <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方法中打个断点,理解这个过程:

image-20240718144509901

点击下一步:

image-20240718144922482

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

image-20240718145229906

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

image-20240718145523282

最后

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

Kolors_00012_

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/843121.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第四周:机器学习笔记

第四周学习周报 摘要Abstract机器学习任务攻略1.loss on training data1.1 training data的loss过大怎么办&#xff1f;1.2 training data的loss小&#xff0c;但是testing data loss大怎么办&#xff1f; 2. 如何选择一个中最好的模型&#xff1f;2.1 Cross Validation&#x…

SpringBoot 最大连接数及最大并发数是多少

SpringBoot 最大连接数及最大并发数 Spring Boot 是一个基于 Spring 框架的快速开发框架&#xff0c;它本身并不直接管理数据库连接或网络连接的最大连接数和最大并发数。这些参数通常由底层的基础设施和组件来控制&#xff0c;例如&#xff1a; 数据库连接池&#xff1a;Spri…

git实操之线上分支合并

线上分支合并 【 1 】本地dev分支合并到本地master上 # 本地dev分支合并到本地master上# 远程(线上)分支合并# 本地dev分支合并到本地master上# 远程(线上)分支合并#####本地和线上分支同步################ #### 远程创建分支&#xff0c;拉取到本地####-远程创建分支&#…

健康问题查询找搜索引擎还是大模型

随着自然语言处理&#xff08;NLP&#xff09;的最新进展&#xff0c;大型语言模型&#xff08;LLMs&#xff09;已经成为众多信息获取任务中的主要参与者。然而&#xff0c;传统网络搜索引擎&#xff08;SEs&#xff09;在回答用户提交的查询中的作用远未被取代。例如&#xf…

PHP手边酒店多商户版平台小程序系统源码

&#x1f3e8;【旅行新宠】手边酒店多商户版小程序&#xff0c;一键解锁住宿新体验&#xff01;&#x1f6cc; &#x1f308;【开篇&#xff1a;旅行新伴侣&#xff0c;尽在掌握】&#x1f308; 还在为旅行中的住宿选择而纠结吗&#xff1f;是时候告别繁琐的搜索和比价过程&a…

Linux——Shell脚本和Nginx反向代理服务器

1. Linux中的shell脚本【了解】 1.1 什么是shell Shell是一个用C语言编写的程序&#xff0c;它是用户使用Linux的桥梁 Shell 既是一种命令语言&#xff0c;有是一种程序设计语言 Shell是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问…

《系统架构设计师教程(第2版)》第12章-信息系统架构设计理论与实践-02-信息系统架构

文章目录 1. 概述1.1 信息系统架构&#xff08;ISA&#xff09;1.2 架构风格 2. 信息系统架构分类2.1 信息系统物理结构2.1.1 集中式结构2.1.2 分布式结构 2.2 信息系统的逻辑结构1&#xff09;横向综合2&#xff09;纵向综合3&#xff09;纵横综合 3. 信息系统架构的一般原理4…

Adobe Premiere Pro(Pr)安装包软件下载

一、简介 Adobe Premiere Pro&#xff08;简称Pr&#xff09;是由Adobe公司开发的一款功能强大的视频编辑软件。它支持多平台使用&#xff0c;包括Windows和Mac系统&#xff0c;并且拥有良好的兼容性和高效的性能。Premiere Pro不仅提供了视频剪辑、特效添加、音频处理等基本功…

【附源码】IMX6U嵌入式Linux开发板连接阿里云--MQTT协议

演示 IMX6U嵌入式Linux开发板连接阿里云 阿里云创建设备&&获取LinkSDK 如果还不知道怎么在阿里云创建设备和获取连接阿里云的LinkSDK的话&#xff0c;先看这篇文章&#xff0c;再到这里。看这篇文章的时候&#xff0c;麻烦将下方文章打开对照着看&#xff0c;因为一些…

pdf提取其中一页怎么操作?提取PDF其中一页的方法

pdf提取其中一页怎么操作&#xff1f;需要从一个PDF文件中提取特定页码的操作通常是在处理文档时常见的需求。这种操作允许用户选择性地获取所需的信息&#xff0c;而不必操作整个文档。通过选择性提取页面&#xff0c;你可以更高效地管理和利用PDF文件的内容&#xff0c;无论是…

ICMP 和 IGMP 的区别

ICMP 和 IGMP 协议 IP 层分支图 ICMP&#xff08;Internet Control Message Protocol&#xff0c;因特网控制信息协议&#xff09; 用于补充 IP 传输数据报的过程中&#xff0c;发送主机无法确定数据报是否到达目标主机。 ICMP 报文分为出错报告报文和查询报文两种。 若数据…

【题解】—— LeetCode一周小结29

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 【题解】—— 每日一道题目栏 上接&#xff1a;【题解】—— LeetCode一周小结28 15.账户合并 题目链接&#xff1a;721. 账户合并 给定一个…

C++初阶:模版初阶【范式编程】【函数模板】【类模板】

一.范式编程 我们在写C函数重载的时候&#xff0c;可能会写许多同一类的函数。 比如交换函数&#xff1a; void Swap(int& left, int& right) {int temp left;left right;right temp; }void Swap(double& left, double& right) {double temp left;left …

环信IM x 亚马逊云科技,助力出海企业实现可靠通讯服务

随着全球化进程的加速&#xff0c;越来越多的企业选择出海&#xff0c;拓展国际市场。然而&#xff0c;面对不同国家和地区的用户&#xff0c;企业在即时通讯方面遇到了诸多挑战。为了帮助企业克服这些困难&#xff0c;环信IM与亚马逊云科技强强联手&#xff0c;共同推出了一套…

One-Class SVM

前提知识&#xff1a;支持向量机&#xff08;SVM&#xff09;-CSDN博客 主要思想 找一个超平面将样本中的正例圈出来&#xff0c;预测就是用这个超平面做决策&#xff0c;在圈内的样本就认为是正样本&#xff0c;圈外的是其他样本&#xff0c;如图1所示&#xff1a; 图1 OSVM…

docker安装jenkins,并配置node和maven

准备 需提前安装好Docker 由于国内docker镜像无法正常使用&#xff0c;需提前做好代理&#xff0c;否则无法正常拉取镜像 开始 拉取jenkins镜像 docker pull jenkins/jenkins:2.468-jdk21 创建一个文件夹&#xff0c;用于二次打包jenkins镜像 mkdir -p /data/jenkins cd /…

神经网络中如何优化模型和超参数调优(案例为tensor的预测)

总结&#xff1a; 初级&#xff1a;简单修改一下超参数&#xff0c;效果一般般但是够用&#xff0c;有时候甚至直接不够用 中级&#xff1a;optuna得出最好的超参数之后&#xff0c;再多一些epoch让train和testloss整体下降&#xff0c;然后结果就很不错。 高级&#xff1a;…

【人工智能】机器学习 -- 贝叶斯分类器

目录 一、使用Python开发工具&#xff0c;运行对iris数据进行分类的例子程序NaiveBayes.py&#xff0c;熟悉sklearn机器实习开源库。 1. NaiveBayes.py 2. 运行结果 二、登录https://archive-beta.ics.uci.edu/ 三、使用sklearn机器学习开源库&#xff0c;使用贝叶斯分类器…

Raid5数据恢复—Raid5热备盘同步失败导致通用卷不可用的数据恢复案例

Raid5算法&#xff1a; Raid5算法也被称为“异或运算”。异或是一个数学运算符&#xff0c;它应用于逻辑运算。异或的数学符号为“⊕”&#xff0c;计算机符号为“xor”。异或的运算法则为&#xff1a;a⊕b (a ∧ b) ∨ (a ∧b)。如果a、b两个值不相同&#xff0c;则异或结果为…

探索XEX数字资产交易的优势与操作指南

随着数字资产市场的快速发展&#xff0c;越来越多的投资者开始关注并参与其中。XEX交易所作为一个新兴的数字资产交易平台&#xff0c;以其用户友好的界面和高效的交易服务&#xff0c;迅速吸引了大量用户。本文将介绍XEX数字资产交易的主要特点和优势&#xff0c;帮助新手更好…