• 04Prism WPF 入门实战 Module


    1.概要

    源码及PPT地址:

    视频地址:source=copyweb

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

     

     

    在Prism体系中Module的应用分为

    • 注册/发现模块
    • 加载模块
    • 初始化模块

     

     

     

    2.详细内容

    • (1)注册/发现模块

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

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

     

     

     

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

     

     

    将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的时候就已经自动创建好了。

     

     

     

    [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.实战应用

     

     

     

     

     

    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("联系人王某");
        }
    }

     

  • 相关阅读:
    如何保证你的路由器安全?
    Jmeter、fiddler、postman 如何模拟ajax请求
    U盘快速格式化和普通格式化有什么区别
    Layui button disabled
    TP连接数据库字符串方式
    Warning: require(D:wamp64wwwxxxpublic/../thinkphp/start.php): failed to open stream: No such file or directory in D:wamp64www xxx publicindex.php on line 17
    [AWS] EC2
    [AWS] EC2
    [AWS] EC2
    [AWS DA
  • 原文地址:https://www.cnblogs.com/justzhuzhu/p/15463227.html
Copyright © 2020-2023  润新知