04Prism WPF 入门实战 - Module

1.概要

源码及PPT地址:https://github.com/JusterZhu/wemail

视频地址:https://www.bilibili.com/video/BV1KQ4y1C7tg?sharesource=copyweb

Module,具有特定功能,且独立存在则称为成为模块。下图为Prism体系中的关系结构图。

522581e77a56b4428ab0ee19cf57cce4.png

在Prism体系中Module的应用分为

  • 注册/发现模块

  • 加载模块

  • 初始化模块

a9d377c2a56691478f10d15538336d1f.png

2.详细内容

  • (1)注册/发现模块

通过重写CreateModuleCatalog方法指定加载module的方式,这里我个人比较推荐使用反射的方式去指定目录下读取,当然还有其他方式这里就不一 一介绍了。

首先我们将项目中的module编译生成到项目运行目录下的Apps文件夹下。

1b1fceaa5e67be011fa379c74aeb97e7.png

这时需要在类库右键->点击属性。

9c57f60f06200455b17797c981c794e5.png

将DLL编译生成时拷贝到,指定目录下(详情见源码)。

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
    /// <summary>
    /// 应用程序启动时创建Shell
    /// </summary>
    /// <returns></returns>
    protected override Window CreateShell()
    {
        return Container.Resolve<MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        //注册服务、依赖、View
    }

    /// <summary>
    /// 配置区域适配
    /// </summary>
    /// <param name="regionAdapterMappings"></param>
    protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
    {
        base.ConfigureRegionAdapterMappings(regionAdapterMappings);
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        //new ConfigurationModuleCatalog()

        //指定模块加载方式为从文件夹中以反射发现并加载module(推荐用法)
        return new DirectoryModuleCatalog() { ModulePath = @".\Apps" };
    }
}
  • (2)加载模块

加载模块的代码实现在DirectoryModuleCatalog内部实现大致如下:

//
// Summary:
//     Represets a catalog created from a directory on disk.
//
// Remarks:
//     The directory catalog will scan the contents of a directory, locating classes
//     that implement Prism.Modularity.IModule and add them to the catalog based on
//     contents in their associated Prism.Modularity.ModuleAttribute. Assemblies are
//     loaded into a new application domain with ReflectionOnlyLoad. The application
//     domain is destroyed once the assemblies have been discovered. The diretory catalog
//     does not continue to monitor the directory after it has created the initialze
//     catalog.
public class DirectoryModuleCatalog : ModuleCatalog
{
    private class InnerModuleInfoLoader : MarshalByRefObject
    {
        internal ModuleInfo[] GetModuleInfos(string path)
        {
            DirectoryInfo directory = new DirectoryInfo(path);
            ResolveEventHandler value = (object sender, ResolveEventArgs args) => OnReflectionOnlyResolve(args, directory);
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += value;
            ModuleInfo[] result = GetNotAlreadyLoadedModuleInfos(IModuleType: AppDomain.CurrentDomain.GetAssemblies().First((Assembly asm) => asm.FullName == typeof(IModule).Assembly.FullName).GetType(typeof(IModule).FullName), directory: directory).ToArray();
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= value;
            return result;
        }

        private static IEnumerable<ModuleInfo> GetNotAlreadyLoadedModuleInfos(DirectoryInfo directory, Type IModuleType)
        {
            List<Assembly> list = new List<Assembly>();
            Assembly[] alreadyLoadedAssemblies = (from p in AppDomain.CurrentDomain.GetAssemblies()
                                                  where !p.IsDynamic
                                                  select p).ToArray();
            foreach (FileInfo item in (from file in directory.GetFiles("*.dll")
                                       where alreadyLoadedAssemblies.FirstOrDefault((Assembly assembly) => string.Compare(Path.GetFileName(assembly.Location), file.Name, StringComparison.OrdinalIgnoreCase) == 0) == null
                                       select file).ToList())
            {
                try
                {
                    list.Add(Assembly.LoadFrom(item.FullName));
                }
                catch (BadImageFormatException)
                {
                }
            }

            return list.SelectMany((Assembly assembly) => from t in assembly.GetExportedTypes().Where(new Func<Type, bool>(IModuleType.IsAssignableFrom))
                                                          where t != IModuleType
                                                          where !t.IsAbstract
                                                          select t into type
                                                          select CreateModuleInfo(type));
        }

        private static Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
        {
            Assembly assembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault((Assembly asm) => string.Equals(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase));
            if (assembly != null)
            {
                return assembly;
            }

            AssemblyName assemblyName = new AssemblyName(args.Name);
            string text = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
            if (File.Exists(text))
            {
                return Assembly.ReflectionOnlyLoadFrom(text);
            }

            return Assembly.ReflectionOnlyLoad(args.Name);
        }

        internal void LoadAssemblies(IEnumerable<string> assemblies)
        {
            foreach (string assembly in assemblies)
            {
                try
                {
                    Assembly.ReflectionOnlyLoadFrom(assembly);
                }
                catch (FileNotFoundException)
                {
                }
            }
        }

        private static ModuleInfo CreateModuleInfo(Type type)
        {
            string name = type.Name;
            List<string> list = new List<string>();
            bool flag = false;
            CustomAttributeData customAttributeData = CustomAttributeData.GetCustomAttributes(type).FirstOrDefault((CustomAttributeData cad) => cad.Constructor.DeclaringType!.FullName == typeof(ModuleAttribute).FullName);
            if (customAttributeData != null)
            {
                foreach (CustomAttributeNamedArgument namedArgument in customAttributeData.NamedArguments)
                {
                    switch (namedArgument.MemberInfo.Name)
                    {
                        case "ModuleName":
                            name = (string)namedArgument.TypedValue.Value;
                            break;
                        case "OnDemand":
                            flag = (bool)namedArgument.TypedValue.Value;
                            break;
                        case "StartupLoaded":
                            flag = !(bool)namedArgument.TypedValue.Value;
                            break;
                    }
                }
            }

            foreach (CustomAttributeData item in from cad in CustomAttributeData.GetCustomAttributes(type)
                                                 where cad.Constructor.DeclaringType!.FullName == typeof(ModuleDependencyAttribute).FullName
                                                 select cad)
            {
                list.Add((string)item.ConstructorArguments[0].Value);
            }

            ModuleInfo obj = new ModuleInfo(name, type.AssemblyQualifiedName)
            {
                InitializationMode = (flag ? InitializationMode.OnDemand : InitializationMode.WhenAvailable),
                Ref = type.Assembly.EscapedCodeBase
            };
            obj.DependsOn.AddRange(list);
            return obj;
        }
    }

    //
    // Summary:
    //     Directory containing modules to search for.
    public string ModulePath
    {
        get;
        set;
    }

    //
    // Summary:
    //     Drives the main logic of building the child domain and searching for the assemblies.
    protected override void InnerLoad()
    {
        if (string.IsNullOrEmpty(ModulePath))
        {
            throw new InvalidOperationException(Prism.Properties.Resources.ModulePathCannotBeNullOrEmpty);
        }

        if (!Directory.Exists(ModulePath))
        {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Prism.Properties.Resources.DirectoryNotFound, ModulePath));
        }

        AppDomain currentDomain = AppDomain.CurrentDomain;
        try
        {
            List<string> list = new List<string>();
            IEnumerable<string> collection = from Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()
                                             where !(assembly is AssemblyBuilder) && assembly.GetType().FullName != "System.Reflection.Emit.InternalAssemblyBuilder" && !string.IsNullOrEmpty(assembly.Location)
                                             select assembly.Location;
            list.AddRange(collection);
            Type typeFromHandle = typeof(InnerModuleInfoLoader);
            if (typeFromHandle.Assembly != null)
            {
                InnerModuleInfoLoader innerModuleInfoLoader = (InnerModuleInfoLoader)currentDomain.CreateInstanceFrom(typeFromHandle.Assembly.Location, typeFromHandle.FullName)!.Unwrap();
                base.Items.AddRange(innerModuleInfoLoader.GetModuleInfos(ModulePath));
            }
        }
        catch (Exception innerException)
        {
            throw new Exception("There was an error loading assemblies.", innerException);
        }
    }
}
  • (3)初始化模块

这些代码在使用Prism项目模板创建Module的时候就已经自动创建好了。

70f24abf400466e6528ab072d3b7696b.png

[Module(ModuleName = "Contact")]
public class ContactModule : IModule
{
    //初始化
    public void OnInitialized(IContainerProvider containerProvider)
    {
        //通过注册RegionManager,添加ContactView
        var regionManager = containerProvider.Resolve<IRegionManager>();
        //通过ContentRegion管理导航默认初始页面ContactView
        var contentRegion = regionManager.Regions["ContentRegion"];
        contentRegion.RequestNavigate(nameof(ContactView));
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterForNavigation<ContactView>();
    }
}

3.实战应用

c58a585bf9ca113eeeff5c09fd8aa5a9.png

2371e06ed9ddb825e684fd04bd4d5330.png

Shell - MainWindow实现

<Window.Resources>
    <Style x:Key="ModuleItemSytle" TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Grid>
                        <TextBlock Text="{Binding ModuleName}"></TextBlock>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="8*"/>
    </Grid.ColumnDefinitions>
    <ListBox ItemsSource="{Binding Modules}" SelectedItem="{Binding ModuleInfo}" ItemContainerStyle="{StaticResource ModuleItemSytle}"  />
    <ContentControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion"/>
</Grid>
</Window>

MainWindowViewModel中的实现

public class MainWindowViewModel : BindableBase
{
    private string _title = "Prism Application";

    //Region管理对象
    private IRegionManager _regionManager;
    private IModuleCatalog _moduleCatalog;
    private ObservableCollection<IModuleInfo> _modules;
    private DelegateCommand _loadModules;
    private IModuleInfo _moduleInfo;

    public IView View { get; set; }

    public string Title
    {
        get { return _title; }
        set { SetProperty(ref _title, value); }
    }

    public ObservableCollection<IModuleInfo> Modules
    {
        get => _modules ?? (_modules = new ObservableCollection<IModuleInfo>());
    }

    public DelegateCommand LoadModules { get => _loadModules = new DelegateCommand(InitModules); }

    public IModuleInfo ModuleInfo 
    { 
        get 
        {
            return _moduleInfo; 
        }

        set 
        {
            _moduleInfo = value;
            Navigate(value);
        }
    }

    public MainWindowViewModel(IRegionManager regionManager, IModuleCatalog moduleCatalog)
    {
        _regionManager = regionManager;
        _moduleCatalog = moduleCatalog;
    }

    public void InitModules() 
    {
        var dirModuleCatalog = _moduleCatalog as DirectoryModuleCatalog;
        Modules.AddRange(dirModuleCatalog.Modules);
    }

    private void Navigate(IModuleInfo info) 
    {
        _regionManager.RequestNavigate("ContentRegion", $"{ info.ModuleName }View");
    }
}

Module - Wemail.Contact实现

ContactModule.cs 模块标记文件

[Module(ModuleName = "Contact")]
public class ContactModule : IModule
{
    public void OnInitialized(IContainerProvider containerProvider)
    {
        //通过注册RegionManager,添加ContactView
        var regionManager = containerProvider.Resolve<IRegionManager>();
        //通过ContentRegion管理导航默认初始页面ContactView
        var contentRegion = regionManager.Regions["ContentRegion"];
        contentRegion.RequestNavigate(nameof(ContactView));
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterForNavigation<ContactView>();
    }
}

ContactView实现

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="8*"/>
    </Grid.ColumnDefinitions>
    <ListBox x:Name="LsbContact" ItemsSource="{Binding Contacts}"/>
    <ContentControl HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="40" Grid.Column="1" Content="{Binding ElementName=LsbContact,Path=SelectedItem}"></ContentControl>
</Grid>

ContactViewModel实现

public class ContactViewModel : BindableBase
{
    private ObservableCollection<string> _contacts;

    private string _message;
    public string Message
    {
        get { return _message; }
        set { SetProperty(ref _message, value); }
    }

    public ObservableCollection<string> Contacts 
    { 
        get => _contacts ?? (_contacts = new ObservableCollection<string>()); 
    }

    public ContactViewModel()
    {
        Message = "Wemail.Contact Prism Module";
        Contacts.Add("联系人张某");
        Contacts.Add("联系人王某");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF Prism是微软新一代的界面开发框架,它具有丰富的功能和灵活的架构,广泛应用于桌面应用程序和Web应用程序的开发中。朝夕停车场项目是基于WPF Prism框架的一个实战项目,它实现了停车场的智能化管理,包括车辆进入和出场的自动识别,车位的实时监测和调度,以及财务管理和数据分析等功能。 该项目的源码包含了多个模块和组件,采用了MVVM架构和依赖注入技术,使代码组织和维护非常方便。项目中的主要模块包括: 1. Shell模块:该模块是整个应用程序的容器,它提供了主窗口和导航栏等界面组件,以及对其他模块的管理和协调。 2. Home模块:该模块实现了停车场的实时监控和调度功能,包括车位的占用和空闲状态显示,车辆进出场的记录和管理,以及停车位的预定和预约等功能。 3. Financial模块:该模块实现了停车场的财务管理和数据分析功能,包括车位租赁、停车费用计算和缴纳,以及停车场运营数据的统计和分析等功能。 4. Configuration模块:该模块实现了停车场的基础配置和参数管理功能,包括车位数量、收费标准和系统设置等功能。 5. Common模块:该模块包含了一些公共的模型和工具类,用于提供系统级别的服务和支持。 通过这个实战项目的源码学习,可以深入了解WPF Prism框架的应用及其MVVM架构和依赖注入的设计思想,也可以了解如何实现一个完整的智能化停车场管理系统。同时,该项目源码可以作为一个参考,通过在此基础上进行二次开发和定制,实现更加具体化的应用需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值