【WPF】Prism P1
视频参考:
https://www.bilibili.com/video/BV1nY411a7T8
博客参考:
https://www.cnblogs.com/prism/archive/2010/07/21/1781855.html
一、元素布局
<Window x:Class="WPF_Practical.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPF_Practical" mc:Ignorable="d" Title="MainWindow" Height="760" Width="1376"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid Grid.Row="0" Grid.Column="0"> <Grid.RowDefinitions> <!-- RowDefinition 适配属性 Height="auto" auto表示自适应 取该定义中的最高高度的元素来撑开内容,如果不存在声明高度的元素 Height="100" 100表示绝对尺寸 Height="2*" 按总栅格占比设置 --> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <!-- ColumnDefinition 适配属性 Width="auto" auto表示自适应 取该定义中的最宽度的元素来撑开内容,如果不存在声明高度的元素 Height="100" 100表示绝对尺寸 Height="2*" 按总栅格占比设置 --> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <!-- 标记这个元素所属在哪个行列定义位置上(从0开始,重复定义则覆盖前置元素的定义) Grid.Row="下标值" Grid.Column="下标值" 标记这个元素需要合并多少行列个数 (被合并的元素如果保持声明则失效) Grid.ColumnSpan="下标值" Grid.RowSpan="下标值" --> <Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Background="Red" /> <Border Grid.Row="0" Grid.Column="0" Background="Blue" /> <Border Grid.Row="1" Grid.Column="0" Background="Yellow" /> <Border Grid.Row="1" Grid.Column="1" Background="Green" /> </Grid> <!-- StackPanel 设置Orientation属性,分为水平 Horizontal 和垂直布局 Vertical 存在缺陷,溢出容器元素后无任何处理 --> <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal"> <Button Width="100" Height="40" Content="Button1" /> <Button Width="100" Height="40" Content="Button2" /> <Button Width="100" Height="40" Content="Button3" /> <Button Width="100" Height="40" Content="Button4" /> <Button Width="100" Height="40" Content="Button5" /> <Button Width="100" Height="40" Content="Button6" /> <Button Width="100" Height="40" Content="Button7" /> <Button Width="100" Height="40" Content="Button8" /> </StackPanel> <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Vertical"> <Button Width="100" Height="40" Content="Button1" /> <Button Width="100" Height="40" Content="Button2" /> <Button Width="100" Height="40" Content="Button3" /> <Button Width="100" Height="40" Content="Button4" /> <Button Width="100" Height="40" Content="Button5" /> <Button Width="100" Height="40" Content="Button6" /> <Button Width="100" Height="40" Content="Button7" /> <Button Width="100" Height="40" Content="Button8" /> <Button Width="100" Height="40" Content="Button9" /> <Button Width="100" Height="40" Content="Button10" /> </StackPanel> <!-- DockPanel 停靠面板 LastChildFill属性, 最后一个元素是否用于填充剩余空间 DockPanel.Dock="方向值" 设置该元素的停靠位置 --> <DockPanel Grid.Row="1" Grid.Column="0" LastChildFill="False"> <Button DockPanel.Dock="Top" Width="100" Height="40" Content="ButtonTop" /> <Button DockPanel.Dock="Bottom" Width="100" Height="40" Content="ButtonBottom" /> <Button DockPanel.Dock="Left" Width="100" Height="40" Content="ButtonLeft" /> <Button DockPanel.Dock="Right" Width="100" Height="40" Content="ButtonRight" /> </DockPanel> <!-- DockPanel 停靠面板 LastChildFill属性, 最后一个元素是否用于填充剩余空间 DockPanel.Dock="方向值" 设置该元素的停靠位置 --> <UniformGrid Grid.Row="1" Grid.Column="1" Columns="3" Rows="3"> <Button Content="Button1" /> <Button Content="Button2" /> <Button Content="Button3" /> <Button Content="Button4" /> <Button Content="Button5" /> <Button Content="Button6" /> <Button Content="Button7" /> <Button Content="Button8" /> <Button Content="Button9" /> </UniformGrid> <UniformGrid Grid.Row="1" Grid.Column="2" Columns="3" Rows="3"> <Button Width="100" Height="40" Content="Button1" /> <Button Width="100" Height="40" Content="Button2" /> <Button Width="100" Height="40" Content="Button3" /> <Button Width="100" Height="40" Content="Button4" /> <Button Width="100" Height="40" Content="Button5" /> <Button Width="100" Height="40" Content="Button6" /> <Button Width="100" Height="40" Content="Button7" /> <Button Width="100" Height="40" Content="Button8" /> <Button Width="100" Height="40" Content="Button9" /> </UniformGrid> <Grid Grid.Row="0" Grid.Column="3"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <WrapPanel Grid.Row="0" Orientation="Horizontal"> <Button Width="100" Height="40" Content="Button1" /> <Button Width="100" Height="40" Content="Button2" /> <Button Width="100" Height="40" Content="Button3" /> <Button Width="100" Height="40" Content="Button4" /> <Button Width="100" Height="40" Content="Button5" /> <Button Width="100" Height="40" Content="Button6" /> <Button Width="100" Height="40" Content="Button7" /> <Button Width="100" Height="40" Content="Button8" /> <Button Width="100" Height="40" Content="Button9" /> </WrapPanel> <WrapPanel Grid.Row="1" Orientation="Vertical"> <Button Width="100" Height="40" Content="Button1" /> <Button Width="100" Height="40" Content="Button2" /> <Button Width="100" Height="40" Content="Button3" /> <Button Width="100" Height="40" Content="Button4" /> <Button Width="100" Height="40" Content="Button5" /> <Button Width="100" Height="40" Content="Button6" /> <Button Width="100" Height="40" Content="Button7" /> <Button Width="100" Height="40" Content="Button8" /> <Button Width="100" Height="40" Content="Button9" /> <Button Width="100" Height="40" Content="Button10" /> </WrapPanel> </Grid> </Grid> </Window>
教程提供的几种布局样式列举:
视频中的后台系统布局面板
布局实现使用行列定义嵌套:
XAML标签代码:
<Window x:Class="WPF_Practical.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPF_Practical" mc:Ignorable="d" Title="MainWindow" Height="760" Width="1376"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="80" /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Border Background="#7471D6" Grid.Row="0" Grid.ColumnSpan="2" /> <Border Background="#F1F2F3" Grid.Row="1" Grid.Column="0" /> <Grid Grid.Row="1" Grid.Column="1"> <Grid.RowDefinitions> <RowDefinition Height="0.65*" /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Border Margin="10" Grid.Row="0" Grid.Column="0" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="0" Grid.Column="1" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="0" Grid.Column="2" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="0" Grid.Column="3" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="0" Grid.Column="4" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="1" Grid.Column="3" Grid.ColumnSpan="2" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Background="#F1F2F3" /> <Border Margin="10" Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="2" Background="#F1F2F3" /> </Grid> </Grid> </Window>
二、Prism框架安装
Step1 NuGet包管理【浏览】搜索 Prism.DryIoc
Step2 扩展部分追加 Prism模版包
【浏览】搜索Prism Template Pack
三、Prism区域和路由
Step1 创建ViewModels目录,编写MainViewModel类
代码部分:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Prism.Commands; using Prism.Mvvm; using WPF_Prism.Views; namespace WPF_Prism.ViewModels { public class MainViewModel : BindableBase { public DelegateCommand<string> OpenCommand { private set; get; } private readonly IRegionManager regionManager; /* 初始化时通过构造器注入区域管理器对象 */ public MainViewModel(IRegionManager regionManager) { /* 创建打开窗口 */ OpenCommand = new DelegateCommand<string>(Open); this.regionManager = regionManager; } private void Open(string obj) { /* 通过区域管理器对象,指定区域名获取该区域对象,并导航到目标视图控件对象上 */ regionManager.Regions["ContentRegion"].RequestNavigate(obj); } } }
Step2 创建Views目录,编写ViewA\ViewB\ViewC三个视图类
控件简单标记下内容以区分是哪一个View
<UserControl x:Class="WPF_Prism.Views.ViewA" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WPF_Prism.Views" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <TextBlock Text="我是ViewA" FontSize="30" /> </Grid> </UserControl>
Step3 把MainWindow更名为MainView,放入到Views中
1 原名称 MainWindow.xaml,放入Views后,更名MainView
2 并且加入Prism的约束,追加自动装配视图支持
3 定义对应三个视图的打开按钮,绑定一个打开命令方法,和对应的参数值
4 定义内容控件,使用区域管理器属性,命名该区域为ContentRegion
<Window x:Class="WPF_Prism.Views.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPF_Prism" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition /> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="ViewA">ViewA</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="ViewB">ViewB</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="ViewC">ViewC</Button> </StackPanel> <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" /> </Grid> </Window>
内部类引用的资源变量名也要调整
using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WPF_Prism.Views; /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainView : Window { public MainView() { InitializeComponent(); } }
Step4 App控件调整
App.xaml.cs代码追加依赖注入
改写为继承PrismApplication
using System.Configuration; using System.Data; using System.Windows; using WPF_Prism.Views; namespace WPF_Prism { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainView>(); } /* 注入视图控件依赖 */ protected override void RegisterTypes(IContainerRegistry containerRegistry) { /* 可以重命名依赖名称 containerRegistry.RegisterForNavigation<ViewA>("ViewAModule"); */ containerRegistry.RegisterForNavigation<ViewA>(); containerRegistry.RegisterForNavigation<ViewB>(); containerRegistry.RegisterForNavigation<ViewC>(); } } }
App.xaml 移除StartURI约束,追加Prism约束
<prism:PrismApplication x:Class="WPF_Prism.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPF_Prism" xmlns:prism="http://prismlibrary.com/"> <Application.Resources> </Application.Resources> </prism:PrismApplication>
Step5 执行预览:
四、Prism模块化
Step1 创建模块A
按正常项目创建,然后项目名称命名为
新创建的项目删除掉这些内容:
然后右键项目名选择【属性】
更改类型为【类库】
在更名为类库后,新建Views目录,创建【用户控件】ViewA
<UserControl x:Class="ModuleA.Views.ViewA" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:ModuleA.Views" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <TextBlock Text="我是ModuleA中的ViewA" FontSize="40" /> </Grid> </UserControl>
NUGET包管理,安装Prism框架,安装方法不赘述了
创建一个模块类声明:
注册区域需要加载的控件资源,
注意,这个模块类,一定要public的才可以
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Prism.Modularity; using ModuleA.Views; namespace ModuleA { public class ModuleAProfile : IModule { public void OnInitialized(IContainerProvider containerProvider) { } public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<ViewA>("M-ViewA"); } } }
Step2 创建模块B
重复上述步骤即可
Step3 主项目引入模块注入
主项目 App.xaml.cs
为了区分是主项目控件AB和模块引用的AB,要声明资源名称来区分
using System.Configuration; using System.Data; using System.Windows; using ModuleA; using ModuleB; using WPF_Prism.Views; namespace WPF_Prism { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainView>(); } /* 注入视图控件依赖 */ protected override void RegisterTypes(IContainerRegistry containerRegistry) { /* 可以重命名依赖名称 containerRegistry.RegisterForNavigation<ViewA>("ViewAModule"); */ containerRegistry.RegisterForNavigation<ViewA>("L-ViewA"); containerRegistry.RegisterForNavigation<ViewB>("L-ViewB"); containerRegistry.RegisterForNavigation<ViewC>(); } /* 引入模块资源进行注入 */ protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<ModuleAProfile>(); moduleCatalog.AddModule<ModuleBProfile>(); base.ConfigureModuleCatalog(moduleCatalog); } } }
在主视图追加模块的AB按钮和对应的参数
<Window x:Class="WPF_Prism.Views.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPF_Prism" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition /> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="L-ViewA">ViewA</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="L-ViewB">ViewB</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="ViewC">ViewC</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="M-ViewA">M-ViewA</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="M-ViewB">M-ViewB</Button> </StackPanel> <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" /> </Grid> </Window>
执行预览:
五、通过指定模块目录注入
Step1 主项目的App.xaml.cs文件改写方法
using System.Configuration; using System.Data; using System.Windows; using WPF_Prism.Views; namespace WPF_Prism { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainView>(); } /* 注入视图控件依赖 */ protected override void RegisterTypes(IContainerRegistry containerRegistry) { /* 可以重命名依赖名称 containerRegistry.RegisterForNavigation<ViewA>("ViewAModule"); */ containerRegistry.RegisterForNavigation<ViewA>("L-ViewA"); containerRegistry.RegisterForNavigation<ViewB>("L-ViewB"); containerRegistry.RegisterForNavigation<ViewC>(); } /* 方式1 引入模块资源进行注入 */ //protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) //{ // moduleCatalog.AddModule<ModuleAProfile>(); // moduleCatalog.AddModule<ModuleBProfile>(); // base.ConfigureModuleCatalog(moduleCatalog); //} /* 方式2 通过指定模块目录读取 */ protected override IModuleCatalog CreateModuleCatalog() { return new DirectoryModuleCatalog() { ModulePath = @".\Modules" }; } } }
Step2 引入模块动态库文件
在主项目这个目录下创建上述声明的模块目录:
C:\Users\Administrator\source\repos\WPF-Prism\WPF-Prism\bin\Debug\net8.0-windows
把另外两个模块的动态库文件拷贝一份到上面的目录里面:
C:\Users\Administrator\source\repos\WPF-Prism\ModuleA\bin\Debug\net8.0-windows
C:\Users\Administrator\source\repos\WPF-Prism\ModuleB\bin\Debug\net8.0-windows
重启预览后发现一样能用:
六、其他方式模块注入
官网代码实例
https://github.com/PrismLibrary/Prism-Samples-Wpf
模块注入是有4种方式,App配置,代码配置,目录配置,手动加载
LoadManual 手动加载
App.xaml.cs
可以看见是把模块资源使用typeof方法转换成元数据对象注入的
using System.Windows; using ModuleA; using Modules.Views; using Prism.Ioc; using Prism.Modularity; using Prism.Unity; namespace Modules { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { var moduleAType = typeof(ModuleAModule); moduleCatalog.AddModule(new ModuleInfo() { ModuleName = moduleAType.Name, ModuleType = moduleAType.AssemblyQualifiedName, InitializationMode = InitializationMode.OnDemand }); } } }
Directory 目录读取
见上文即可,这里不再赘述
Code 硬编码配置
App.xaml.cs 是把具体视图资源引用了
using Modules.Views; using Prism.Ioc; using Prism.Modularity; using Prism.Unity; using System.Windows; namespace Modules { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<ModuleA.ModuleAModule>(); } } }
但是模块类声明,是在初始化处理了:
由容器提供器来解析完成
using ModuleA.Views; using Prism.Ioc; using Prism.Modularity; using Prism.Navigation.Regions; namespace ModuleA { public class ModuleAModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve<IRegionManager>(); regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } }
AppConfig App配置
多出两个配置文件:
App.config
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf" /> </configSections> <startup> </startup> <modules> <module assemblyFile="ModuleA.dll" moduleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleAModule" startupLoaded="True" /> </modules> </configuration>
ModuleCatalog.xaml
<m:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:m="clr-namespace:Prism.Modularity;assembly=Prism.Wpf"> <m:ModuleInfo ModuleName="ModuleAModule" ModuleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </m:ModuleCatalog>
App.xaml.cs 改用了Xaml方式读取
using Modules.Views; using Prism.Modularity; using Prism.Unity; using Prism.Ioc; using System.Windows; using System; namespace Modules { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override IModuleCatalog CreateModuleCatalog() { return new XamlModuleCatalog(new Uri("/Modules;component/ModuleCatalog.xaml", UriKind.Relative)); } } }
七、导航
一、路由参数传递
主项目App.xaml.cs 把模块方式改为编码方式添加
方式2需要手动更新动态库文件,没仔细看还以为哪个步骤写错了
using System.Configuration; using System.Data; using System.Windows; using ModuleB; using ModuleA; using WPF_Prism.Views; namespace WPF_Prism { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainView>(); } /* 注入视图控件依赖 */ protected override void RegisterTypes(IContainerRegistry containerRegistry) { /* 可以重命名依赖名称 containerRegistry.RegisterForNavigation<ViewA>("ViewAModule"); */ containerRegistry.RegisterForNavigation<ViewA>("L-ViewA"); containerRegistry.RegisterForNavigation<ViewB>("L-ViewB"); containerRegistry.RegisterForNavigation<ViewC>(); } /* 方式1 引入模块资源进行注入 */ protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<ModuleAProfile>(); moduleCatalog.AddModule<ModuleBProfile>(); base.ConfigureModuleCatalog(moduleCatalog); } /* 方式2 通过指定模块目录读取 */ //protected override IModuleCatalog CreateModuleCatalog() //{ // return new DirectoryModuleCatalog() // { // ModulePath = @".\Modules" // }; //} } }
MainViewModel.cs类的导航方法追加路由参数:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Prism.Commands; using Prism.Mvvm; using WPF_Prism.Views; namespace WPF_Prism.ViewModels { public class MainViewModel : BindableBase { public DelegateCommand<string> OpenCommand { private set; get; } private readonly IRegionManager regionManager; /* 初始化时通过构造器注入区域管理器对象 */ public MainViewModel(IRegionManager regionManager) { /* 创建打开窗口 */ OpenCommand = new DelegateCommand<string>(Open); this.regionManager = regionManager; } private void Open(string obj) { /* 可以向路由传递参数 */ NavigationParameters keys = new NavigationParameters(); keys.Add("Title", "nav-para"); /* 通过区域管理器对象,指定区域名获取该区域对象,并导航到目标视图控件对象上, 附带路由参数 */ regionManager.Regions["ContentRegion"].RequestNavigate(obj, keys); } } }
模块A需要添加Prism的约束配置:
这里绑定一个Title以方便展示传递的路由参数
<UserControl x:Class="ModuleA.Views.ViewA" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:prism="http://prismlibrary.com/" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <StackPanel> <TextBlock Text="我是ModuleA中的ViewA" FontSize="40" /> <TextBlock Text="{ Binding Title }" FontSize="40" /> </StackPanel> </Grid> </UserControl>
创建ViewA对应的Model类,注意目录一定要ViewModels名称
且类名为ViewAViewModel,这样可支持自动装配识别注入
但是视频还是改用编码方式注入:
ModuleAProfile.cs 追加对ViewAViewModel类的注入
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Prism.Modularity; using ModuleA.Views; using ModuleA.ViewModels; namespace ModuleA { public class ModuleAProfile : IModule { public void OnInitialized(IContainerProvider containerProvider) { } public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>("M-ViewA"); } } }
ViewAViewModel 继承和实现了 BindableBase, INavigationAware
Aware接口实现,会有一个回调支持,当跳转到此控件时,传递路由的上下文
可以获取到路由参数信息
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ModuleA.ViewModels { class ViewAViewModel : BindableBase, INavigationAware { public ViewAViewModel() { } public bool IsNavigationTarget(NavigationContext navigationContext) { return true; } private string title; public string Title { get { return title; } set { title = value; RaisePropertyChanged(); } } public void OnNavigatedFrom(NavigationContext navigationContext) { } public void OnNavigatedTo(NavigationContext navigationContext) { if (navigationContext.Parameters.ContainsKey("Title")) Title = navigationContext.Parameters.GetValue<string>("Title"); } } }
点击模块A的ViewA预览,可以发现参数能接收到:
八、跳转拦截
ViewAViewModel.cs文件调整实现接口
追加一个处理方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; namespace ModuleA.ViewModels { class ViewAViewModel : BindableBase, IConfirmNavigationRequest // INavigationAware { public ViewAViewModel() { } public bool IsNavigationTarget(NavigationContext navigationContext) { return true; } private string title; public string Title { get { return title; } set { title = value; RaisePropertyChanged(); } } public void OnNavigatedFrom(NavigationContext navigationContext) { } public void OnNavigatedTo(NavigationContext navigationContext) { if (navigationContext.Parameters.ContainsKey("Title")) Title = navigationContext.Parameters.GetValue<string>("Title"); } /* 路由从此页面跳转至下一个页面时拦截 */ public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback) { bool result = true; if (MessageBox.Show("确认导航?", "温馨提示", MessageBoxButton.YesNo) == MessageBoxResult.No) result = false; continuationCallback(result); } } }
当要从当前模块A的ViewA跳转至下一页面时(包括跳转至自己时),进行拦截:
九、路由日志对象
MainViewModel.cs 文件
1、每次跳转时可以通过路由上下文拿到Journal对象,这个对象不需要频繁赋值,一次就够了
2、返回上一页是通过Journal的GoBack()方法实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Prism.Commands; using Prism.Mvvm; using WPF_Prism.Views; namespace WPF_Prism.ViewModels { public class MainViewModel : BindableBase { public DelegateCommand<string> OpenCommand { private set; get; } public DelegateCommand BackCommand { private set; get; } private readonly IRegionManager regionManager; private IRegionNavigationJournal journal; /* 初始化时通过构造器注入区域管理器对象 */ public MainViewModel(IRegionManager regionManager) { /* 创建打开窗口 */ OpenCommand = new DelegateCommand<string>(Open); BackCommand = new DelegateCommand(Back); this.regionManager = regionManager; } private void Open(string obj) { /* 可以向路由传递参数 */ NavigationParameters keys = new NavigationParameters(); keys.Add("Title", "nav-para"); /* 通过区域管理器对象,指定区域名获取该区域对象,并导航到目标视图控件对象上, 附带路由参数 */ regionManager.Regions["ContentRegion"].RequestNavigate(obj, navigationCallback => { /* 只需要传递一次即可 */ if (journal == null && navigationCallback != null && navigationCallback.Success) journal = navigationCallback.Context.NavigationService.Journal; }, keys); } private void Back() { if (journal != null && journal.CanGoBack) { journal.GoBack(); } } } }
主窗体界面追加返回上一步:
<Window x:Class="WPF_Prism.Views.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPF_Prism" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition /> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="L-ViewA">ViewA</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="L-ViewB">ViewB</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="ViewC">ViewC</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="M-ViewA">M-ViewA</Button> <Button Margin="5" Command="{ Binding OpenCommand }" CommandParameter="M-ViewB">M-ViewB</Button> <Button Margin="5" Command="{ Binding BackCommand }">上一页</Button> </StackPanel> <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" /> </Grid> </Window>
效果预览: