WPF入门到跪下 第十一章 Prism(八)模块化管理-Module

模块化管理-Model

Module是功能和资源的逻辑集合,其打包方式可以单独开发、测试、部署并集成到应用程序中。
当随着项目较为庞大,有大量的不同业务、ViewViewModel时,就需要考虑根据不同业务进行程序集的拆分了,而Module就是帮助我们管理不同的程序集并集成到应用程序中。
每个模块化管理的程序集中都有一个模块核心类,IModule的子类。主程序集通过加载这个IModule的子类,就可以将对应程序集集成到主程序集的prism框架中来。

一、简单使用

这里以主程序使用其他模块管理的程序集中的Region视图为例进行Module的简单使用。
首先在主程序集所在的解决方案中添加新的WPF用户控件库、创建Views与ViewModels文件夹、引入prism.Unity库。
在这里插入图片描述

①View层

在Views中添加将要使用用户控件ViewA

  • ViewA.xaml

    <UserControl ......>
        <Grid>
            <StackPanel>
                <TextBlock Text="{Binding Value}"/>
            </StackPanel>
        </Grid>
    </UserControl>
    

②ViewModel层

在ViewModels中创建对应的视图模型ViewAViewModel

  • ViewAViewModel.cs

    public class ViewAViewModel:BindableBase
    {
        private string _value = "这里是ViewA";
    
        public string Value
        {
            get { return _value; }
            set 
            {
                SetProperty(ref _value, value); 
            }
        }
    }
    

③实现IModule

在程序集中创建SubModule实现IModule接口,其内含以下两个函数:

  • OnInitialized(IContainerProvider containerProvider):当模块初始化时调用,可以处理一些业务逻辑。
  • RegisterTypes(IContainerRegistry containerRegistry):进行类型注册,与主程序集中App后台代码的RegisterTypes函数作用差不多的。

RegisterTypes函数中,通过IContainerRegistry对象的RegisterForNavigation方法注册ViewA类型。

OnInitialized函数中,进行区域的初始化加载,如果使用模块化管理,那么RegionView的初始化加载时机最好是放在Module中执行,这样的好处在于,当主程序无法顺利加载Module时,不会因此而抛出异常。而且思路跟整体性会较好。

  • SubModule.cs

    public class SubModule : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
            //在这里进行区域的初始化加载是一个比较好的做法
    				var regionManager = containerProvider.Resolve<IRegionManager>();
            regionManager.RegisterViewWithRegion("LeftMenuTreeRegion", typeof(TreeMenuView)); //LeftMenuTreeRegion是主程序中的Region名
    				//此外,模块初始化时可以在这里进行一些业务处理
    				
        }
    
        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            //进行类型注册,与主程序集中App的RegisterTypes函数作用差不多的。
            //containerRegistry.RegisterForNavigation<Views.ViewA>();  这里由于OnInitialized函数中使用了RegisterViewWithRegion方法来加载区域,所以不用再注册
        }
    }
    

④加载Module

在主程序集的App后台代码中重写ConfigureModuleCatalog方法,并通过该方法的参数IModuleCatalog对象的AddModule方法进行Module类型的注册。

AddModule<T>():加载Module类型,泛型T为要加载的Module类型。

  • App.xaml.cs

    public partial class App : PrismApplication
    {
        ......
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule<Sub.SubModule>();
        }
    }
    

⑤使用视图

在主程序集中正常使用即可

  • MainWindow.xaml

    <Window ......
            xmlns:prism="http://prismlibrary.com/">
        <Grid>
            <StackPanel>
                <Button Content="ModuleButton" Command="{Binding BtnCommand}"/>
                <ContentControl prism:RegionManager.RegionName="MainRegion"/>
            </StackPanel>
        </Grid>
    </Window>
    
  • MainWindowViewModel.cs

    public class MainWindowViewModel:BindableBase
    {
        [Dependency]
        public IRegionManager regionManager { get; set; }
        public ICommand BtnCommand { 
            get => new DelegateCommand(() =>
            {
                regionManager.RequestNavigate("MainRegion", "ViewA");
            }); 
        }
    }
    

二、4种Module加载方式

1、AddModule的泛型方法

在主程序集的App后台代码中重写ConfigureModuleCatalog方法,并通过该方法的参数IModuleCatalog对象的AddModule<T>方法进行Module类型的加载。

AddModule<T>():加载Module类型,泛型T为要加载的Module类型。

上文例子中使用的就是这种方法,这里就不再举例了。

2、AddModule的非泛型方法

在主程序集的App后台代码中重写ConfigureModuleCatalog方法,并通过该方法的参数IModuleCatalog对象的非泛型函数AddModule进行Module类型的加载。

AddModule(IModuleInfo moduleInfo):加载Module类型,需要加载的Module类型的信息存放在参数moduleInfo中。

  • App.xaml.cs

    public partial class App : PrismApplication
    {
    ......
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            Type type = typeof(Sub.SubModule);
            moduleCatalog.AddModule(new ModuleInfo { 
                ModuleName = "Hello",
                ModuleType = type.AssemblyQualifiedName
            });
        }
    }
    

需要注意,创建ModuleInfo类型时,ModuleName必须进行初始化,否则会报错。

3、配置文件方式

配置文件的方式可以使用App.config进行配置,也可以使用xml文件的方式。

①Module配置

App.config方式:在主程序集的应用配置文件App.config(如果没有则添加)中进行module配置。

  • App.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    	<configSections>
    		<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/>
    	</configSections>
    	<modules>
    		<module assemblyFile="PrismModule.Sub"
    				moduleType="PrismModule.Sub.SubModule, PrismModule.Sub, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    				moduleName="ViewAModule"/>
    	</modules>
    </configuration>
    

XML文件方式:在主程序集下新建ModuleConfig文件夹并在文件夹内创建ModuleConfig.xml进行配置,然后将该文件属性设置为资源。

  • 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="ViewAModule"
                      ModuleType="PrismModule.Sub.SubModule, PrismModule.Sub, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </m:ModuleCatalog>
    

PS:moduleType看起来很长,但不需要死记,其实就是方式2中的ModuleType

②加载模块

在主程序集的App后台代码中重写CreateModuleCatalog方法并返回一个新创的ConfigurationModuleCatalog对象即可。

  • App.xaml.cs

    public partial class App : PrismApplication
    {
        ......
        protected override IModuleCatalog CreateModuleCatalog()
    		{
    				//App.config配置方式的module加载
    		    return new ConfigurationModuleCatalog();
    				//xml文件配置方式的module加载
    		    //return new XamlModuleCatalog(new Uri("/PrismModule.Sub;component/ModuleConfig/ModuleConfig.xml"));
    		}
    }
    

4、扫描指定目录方式(常用)

除了上面三种加载方式外,还有一种更加解耦、更加方便的方式,就是指定Module所在目录,让程序自动去扫描并加载Module,做法很简单,只需要在主程序集的App后台代码中重写CreateModuleCatalog方法并返回一个包含Module存放路径信息的DirectoryModuleCatalog对象即可。

需要注意,这种方式要在编译后将需要使用的模块程序集的dll文件放置到对应目录下,一般会在Debug目录下新建Moduls,并将编译后的需要使用的模块对应程序集dll文件放入其中
在这里插入图片描述

  • App.xaml.cs

    public partial class App : PrismApplication
    {
        ......
        protected override IModuleCatalog CreateModuleCatalog()
        {
            return new DirectoryModuleCatalog()
            {
               //表示扫描程序运行目录下的Modules文件夹,也就是Debug最里层目录下的Modules文件夹
                ModulePath = ".\\Modules"
            };
        }
    }
    

使用这种方式的好处在于,可以最大程度的让主程序集跟其他模块程序集保持解耦状态。

  • 程序集编译成功后自动将dll文件放入指定目录的设置

    进入程序集的属性设置,在生成后事件中输入copy $(TargetPath) $(SolutionDir)Client\bin\Debug\net6.0-windows\Modules\$(TargetFileName) /Y,其中Client\bin\Debug\net6.0-windows\Modules\为解决方案下的指定目录在这里插入图片描述

三、按需加载(懒加载)

Prism框架中,对于Module的使用默认情况下是采用预加载的,但也提供了懒加载的方案,只需要在添加Module类型的时候设置为懒加载,然后在使用module前通过IModuleManager对象的LoadModule(moduleName)方法进行加载即可。

1、懒加载设置

上文学习的4种加载方式都能设置为懒加载,具体如下:

AddModule的泛型方式

AddModule<T>(string name, InitializationMode mode):加载模块,T为要加载的模块类型。

  • name:主动加载模块时使用的模块名称。

  • mode:加载模式,默认为预加载模式,InitializationMode.OnDemand为懒加载。

  • 示例

    
    public partial class App : PrismApplication
    {
        ......
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule<Sub.SubModule>("ViewAModule", InitializationMode.OnDemand);
        }
    }
    

AddModule的非泛型方式

创建ModuleInfo时,在初始化器中将InitializationMode属性设置为InitializationMode.OnDemand

  • 示例

    public partial class App : PrismApplication
    {
        ......
    
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule(new ModuleInfo
            {
                ModuleName = "ViewAModule",
                ModuleType = typeof(Sub.SubModule).AssemblyQualifiedName,
                InitializationMode = InitializationMode.OnDemand
            });
        }
    }
    

配置文件方式

在配置文件的module节点上,设置属性startupLoaded="false”

  • 示例-App.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    	<configSections>
    		<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/>
    	</configSections>
    	<modules>
    		<module assemblyFile="PrismModule.Sub"
    				moduleType="PrismModule.Sub.SubModule, PrismModule.Sub, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    				moduleName="ViewAModule"
    				startupLoaded="false"/>
    	</modules>
    </configuration>
    

扫描目录方式

扫描目录的方式要实现懒加载需要在对应的IModule实现类上使用特性[Module(ModuleName ="ViewAModule", OnDemand =true)]进行标识

  • 示例

    [Module(ModuleName ="ViewAModule", OnDemand =true)]
    public class SubModule : IModule
    {
        ......
    }
    

2、使用前进行加载

通过IOC依赖注入获得IModuleManager对象属性,然后调用LoadModule方法即可。

  • 示例

    public class MainWindowViewModel:BindableBase
    {
        [Dependency]
        public IRegionManager regionManager { get; set; }
        [Dependency]
        public IModuleManager moduleManager { get; set; }
        public ICommand BtnCommand { 
            get => new DelegateCommand(() =>
            {
                moduleManager.LoadModule("ViewAModule");
                regionManager.RequestNavigate("MainRegion", "ViewA");
            }); 
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SchuylerEX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值