找回密码
 立即注册
首页 业界区 业界 WPF Stylet可以如何实现导航功能?

WPF Stylet可以如何实现导航功能?

越蔓蔓 2025-8-15 09:49:57
1.jpeg

前言

本文是学习Stylet中导航Demo的总结,希望对你有所帮助。
Demo所在的位置:
2.png

先看一下导航的效果:
首页
3.png

通过上面导航到Page 2:
4.png

通过Page1导航到Page2:
5.png

Stylet是如何实现导航的?

先来看一下页面布局:
6.png

一共有ShellView、HeaderView、Page1View与Page2View一共四个View。
ShellView的xaml如下:
  1. <Window x:
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.         xmlns:local="clr-namespace:Stylet.Samples.NavigationController.Pages"
  7.         mc:Ignorable="d"
  8.         Title="Navigation Controller sample" Height="450" Width="800"
  9.         xmlns:s="https://github.com/canton7/Stylet"
  10.         d:DataContext="{d:DesignInstance local:ShellViewModel}">
  11.     <DockPanel>
  12.         <ContentControl DockPanel.Dock="Top" s:View.Model="{Binding HeaderViewModel}"/>
  13.         <ContentControl s:View.Model="{Binding ActiveItem}"/>
  14.     </DockPanel>
  15. </Window>
复制代码
页面的上部分通过s:View.Model="{Binding HeaderViewModel}"绑定到了HearView。
下部分通过s:View.Model="{Binding ActiveItem}"绑定到了激活项的View。
这里你可能会感到疑惑,ActiveItem这个属性是哪里来的呢?
ActiveItem这个属性是在ConductorBaseWithActiveItem中定义的:
7.png

ShellViewModel继承 Conductor, Conductor继承 ConductorBaseWithActiveItem。
这里你就把ActiveItem理解成导航激活的那个ViewModel就行了,这个例子中要么是Page1ViewModel要么就是Page2ViewModel。
现在来看一下NavigationController:
  1. public class NavigationController : INavigationController
  2. {
  3.     private readonly Func<Page1ViewModel> page1ViewModelFactory;
  4.     private readonly Func<Page2ViewModel> page2ViewModelFactory;
  5.     public INavigationControllerDelegate Delegate { get; set; }
  6.     public NavigationController(Func<Page1ViewModel> page1ViewModelFactory, Func<Page2ViewModel> page2ViewModelFactory)
  7.     {
  8.         this.page1ViewModelFactory = page1ViewModelFactory ?? throw new ArgumentNullException(nameof(page1ViewModelFactory));
  9.         this.page2ViewModelFactory = page2ViewModelFactory ?? throw new ArgumentNullException(nameof(page2ViewModelFactory));
  10.     }
  11.     public void NavigateToPage1()
  12.     {
  13.         this.Delegate?.NavigateTo(this.page1ViewModelFactory());
  14.     }
  15.     public void NavigateToPage2(string initiator)
  16.     {
  17.         Page2ViewModel vm = this.page2ViewModelFactory();
  18.         vm.Initiator = initiator;
  19.         this.Delegate?.NavigateTo(vm);
  20.     }
  21. }
复制代码
看一下INavigationController:
  1. public interface INavigationController
  2. {
  3.     void NavigateToPage1();
  4.     void NavigateToPage2(string initiator);
  5. }
复制代码
首先解决一个疑问,这里为什么使用private readonly Func page1ViewModelFactory;而不是直接使用Page1ViewModel呢?
我们知道在C#中Func表示一个没有参数,返回值为Page1ViewModel的委托。
再看看Bootstrapper中的ConfigureIoC方法:
8.png

这样写的目的就是不是一开始就将Page1ViewModel与Page2ViewModel注入进来,而是在使用的时候才注入进来。
我们发现在NavigationController中具体实现导航是通过INavigationControllerDelegate接口实现的,让我们再来看看这个接口:
  1. public interface INavigationControllerDelegate
  2. {
  3.     void NavigateTo(IScreen screen);
  4. }
复制代码
回到ShellViewModel,我们发现它实现了这个接口。
9.png

来看下它的实现:
  1. public void NavigateTo(IScreen screen)
  2. {
  3.      this.ActivateItem(screen);
  4. }
复制代码
使用的是 Conductor中的ActivateItem方法:
10.png

当我们从页面1导航到页面2时:
11.png

由于要导航去的Page2ViewModel不是当前的激活项Page1ViewModel,就会来到ChangeActiveItem方法:
12.png

关闭之前的激活项,设置新的激活项。
就成功导航到Page2ViewModel了,然后根据Page2ViewModel就会找到Page2View了,这样就成功实现导航功能了。
最后再来看一下有一个循环依赖问题:
13.png

这里存在一个循环依赖关系:ShellViewModel -> HeaderViewModel -> NavigationController -> ShellViewModel。
如果直接在NavigationController的构造函数中注入ShellViewModel就会引发这个循环依赖问题。
作者通过在构建 NavigationController 后,再将 ShellViewModel 赋值给它的方式来打破这一循环依赖。
最后

Stylet导航功能的实现主要是通过Conductor实现的。
从作者的这个示例中学习了如何使用Stylet实现一个导航应用,还是学习到了很多知识的,感谢作者的付出!!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册