
WPF Prism 复合命令与模块化管理
Prism 中的 CompositeCommand 示例
CompositeCommand 是 Prism 框架中用于组合多个子命令(DelegateCommand 实例)的类。它允许你将多个命令表示为单个命令,或者在需要时调用多个命令来实现一个逻辑命令。
示例代码
这里做一个简单的示例,注册父窗体中的按钮命令,让子窗口进行调用。
首先我们定义一个复合命令类,并在App.xaml.cs
中注册单例全局。
namespace LearningRegionPrism.Commands
{
public class MyApplicationCommands
{
private CompositeCommand _saveCommand = new CompositeCommand();
public CompositeCommand SaveCommand
{
get => _saveCommand;
}
}
}
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindowView>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<TestRegionWindowView>();
containerRegistry.RegisterForNavigation<MMView>();
// 在这里注册
containerRegistry.RegisterSingleton<MyApplicationCommands>();
}
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
regionAdapterMappings.RegisterMapping<Canvas>(Container.Resolve<CanvasRegionAdapter>());
}
}
然后我们在MainWindowView.xaml
中添加一个TextBlock
标签,用于绑定MainWindowViewModel
中的Msg
。
<TextBlock Text="{Binding Msg}" Grid.ColumnSpan="2" VerticalAlignment="Center"/>
然后我们在MainWindowViewModel
类中定义Msg
属性,并且添加一个UpdateMessageCommand
命令并将其在构造函数中进行注册到复合命令。
public class MainWindowViewModel : BindableBase
{
private string _Msg = "Bob 子窗口";
public string Msg
{
get { return _Msg; }
set { SetProperty(ref _Msg, value); }
}
public MainWindowViewModel(MyApplicationCommands commands)
{
// 注册
commands.SaveCommand.RegisterCommand(UpdateMessageCommand);
}
public ICommand UpdateMessageCommand
{
get => new DelegateCommand(() =>
{
Msg = "从子窗体进行了修改";
});
}
}
然后我们在MMView.xaml
中添加一个按钮并绑定ParentBtnCommand
命令。
<Button Content="Exect Parent Command" Command="{Binding ParentBtnCommand}"/>
在MMViewModel
中,定义一个复合命令属性,然后从MyApplicationCommands
中获取属性,进行调用尝试。
public CompositeCommand ParentBtnCommand { get; private set; }
public MMViewModel(IRegionNavigationService regionNavigationService,MyApplicationCommands commands)
{
ParentBtnCommand = commands.SaveCommand;
}
我们可以看到它对父窗口进行修改。
模块化管理
在 Prism 框架中,模块化开发是一种核心设计理念,它通过将应用程序划分为多个独立的模块,帮助开发者更好地组织代码,实现松耦合的架构设计。本文将通过一个简单的示例,详细介绍如何在 Prism 中实现模块化开发。
简单示例
打开 Visual Studio,创建一个新的 WPF 用户控件库项目,命名为 LearningPrismControlLibrary
。
并添加上Prism
相关包。
<ItemGroup>
<PackageReference Include="Prism.Core" Version="9.0.537" />
<PackageReference Include="Prism.Unity" Version="9.0.537" />
<PackageReference Include="Prism.Wpf" Version="9.0.537" />
</ItemGroup>
创建一个简单的视图ViewA
和ViewAViewModel
。
<UserControl x:Class="LearningPrismControlLibrary.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:LearningPrismControlLibrary.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="{Binding Msg}"/>
</Grid>
</UserControl>
public class ViewAViewModel : BindableBase
{
private string _Msg = "100";
public string Msg
{
get { return _Msg; }
set { SetProperty(ref _Msg, value); }
}
}
接下来我们定义一下我们的SubModule
模块类,并将我们的ViewA
注册到SubModule
中。
namespace LearningPrismControlLibrary
{
public class SubModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
}
在我们LearningRegionPrism
项目中添加相关的用户控件类库,然后在App.xaml.cs
中添加我们的模块。
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<SubModule>();
}
在MainWindowView
中我们在构造中显示并注册ViewA
视图到MainContent
区域中。
public MainWindowView(IRegionManager regionManager)
{
InitializeComponent();
regionManager.RegisterViewWithRegion<ViewA>("MainContent");
}
然后我们运行测试一下,发现没有问题。
按需加载模块的方式
当我们需要ViewA
的时候加载该模块,不需要则可以不用加载。
可以按照如下方式操作,首先修改ConfigureModuleCatalog
方法:
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
// 名称加载
// moduleCatalog.AddModule<SubModule>();
// 第二种加载方式
var typename = typeof(SubModule);
moduleCatalog.AddModule(new ModuleInfo() {
ModuleName = "hello",
// 设置模块类型为 SubModule 类的程序集限定名,ModuleType 指定模块的完整类型信息,包括命名空间和程序集名称,Prism 根据此信息来实例化模块
ModuleType = typename.AssemblyQualifiedName,
// 设置模块的初始化模式为按需加载,InitializationMode.OnDemand 表示模块不会在应用程序启动时自动初始化,而是在需要时才进行初始化,这样可以提高应用程序的启动性能
InitializationMode = InitializationMode.OnDemand
});
}
在MainWindowView
中注释掉注册的视图的代码。
//regionManager.RegisterViewWithRegion<ViewA>("MainContent");
在MainWindowView.xaml
中我们添加一个新的按钮,当点击时加载模块然后显示ViewA
区域。
<Button Content="Module Button" Command="{Binding ModuleBtnCommand}" CommandParameter="ViewA"/>
[Dependency]
public IModuleManager moduleManager { get; set; }
public ICommand ModuleBtnCommand
{
get => new DelegateCommand<string>((x) =>
{
moduleManager.LoadModule("hello");
regionManager.RequestNavigate("MainContent",x);
});
}
配置文件添加模块
添加App.config
配置文件,并进行如下配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/>
</configSections>
<modules>
<module
assemblyFile="LearningPrismControlLibrary"
moduleType="LearningPrismControlLibrary.SubModule, LearningPrismControlLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
moduleName="hello" startupLoaded="false">
</module>
</modules>
</configuration>
修改App.xaml.cs
,模块的创建改为配置文件创建。
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
// 名称加载
//moduleCatalog.AddModule<SubModule>();
// 第二种加载方式
//var typename = typeof(SubModule);
//moduleCatalog.AddModule(new ModuleInfo()
//{
// ModuleName = "hello",
// // 设置模块类型为 SubModule 类的程序集限定名,ModuleType 指定模块的完整类型信息,包括命名空间和程序集名称,Prism 根据此信息来实例化模块
// ModuleType = typename.AssemblyQualifiedName,
// // 设置模块的初始化模式为按需加载,InitializationMode.OnDemand 表示模块不会在应用程序启动时自动初始化,而是在需要时才进行初始化,这样可以提高应用程序的启动性能
// InitializationMode = InitializationMode.OnDemand
//});
// 配置文件添加模块
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
自定义配置文件添加模块
首先我们创建一个ModuleConfigs
文件夹,在下面创建一个我们自定义的ModuleConfig.xml
模块配置文件,文件内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<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="hello"
ModuleType="LearningPrismControlLibrary.SubModule, LearningPrismControlLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
InitializationMode="OnDemand"
/>
</m:ModuleCatalog>
注意需要将这个文件设置为资源类型的文件。
然后我们修改App.xaml.cs
下的CreateModuleCatalog
方法。
protected override IModuleCatalog CreateModuleCatalog()
{
//return new ConfigurationModuleCatalog();
var url = new Uri("/LearningRegionPrism;component/ModuleConfigs/ModuleConfig.xml"
, UriKind.RelativeOrAbsolute);
// 第二种方式自定义配置文件加载方式
return new XamlModuleCatalog(url);
}
扫描目录加载模块
通过扫描某个目录进行加载模块。
首先我们需要给我们的模块添加上模块名称。
namespace LearningPrismControlLibrary
{
[Module(ModuleName = "hello",OnDemand =true)]
public class SubModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
}
然后我们修改App.xaml.cs
下的CreateModuleCatalog
方法,使用目录扫描的方式进行加载模块,这里我们扫描Mods
文件夹下面的模块。
protected override IModuleCatalog CreateModuleCatalog()
{
//return new ConfigurationModuleCatalog();
//var url = new Uri("/LearningRegionPrism;component/ModuleConfigs/ModuleConfig.xml"
// , UriKind.RelativeOrAbsolute);
//// 第二种方式自定义配置文件加载方式
//return new XamlModuleCatalog(url);
// 第三种扫描目录的方式
return new DirectoryModuleCatalog() { ModulePath = "./Mods" };
}
然后我们重新生成我们的代码,然后在输出目录创建一个Mods
文件夹,将我们的模块文件放在这个下面:
运行测试一下。
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

