インターフェース
サービス
モジュール
ModuleAModule.cs
1using System.Windows;
2using ModuleA.Views;
3using PluginServices.Interfaces;
4using Prism.Ioc;
5using Prism.Modularity;
6
7namespace ModuleA
8{
9 public class ModuleAModule : IModule
10 {
11 public void OnInitialized(IContainerProvider containerProvider)
12 {
13 var pluginMenu = containerProvider.Resolve<IPluginMenuService>();
14 pluginMenu.ModuleMenus.Add(new() { Header = "モジュールA", ViewName = nameof(ViewA) });
15 }
16
17 public void RegisterTypes(IContainerRegistry containerRegistry)
18 {
19 containerRegistry.RegisterForNavigation<ViewA>();
20 }
21 }
22}
ModuleBModule.cs
1namespace ModuleB
2{
3 public class ModuleBModule : IModule
4 {
5 public void OnInitialized(IContainerProvider containerProvider)
6 {
7 var pluginMenu = containerProvider.Resolve<IPluginMenuService>();
8 pluginMenu.ModuleMenus.Add(new() { Header = "モジュールB", ViewName = nameof(ViewB) });
9 }
10
11 public void RegisterTypes(IContainerRegistry containerRegistry)
12 {
13 containerRegistry.RegisterForNavigation<ViewB>();
14 }
15 }
16}
PluginAModule.cs
1using System.Windows;
2using PluginA.Views;
3using PluginServices.Interfaces;
4using Prism.Ioc;
5using Prism.Modularity;
6
7namespace PluginA
8{
9 public class PluginAModule : IModule
10 {
11 public void OnInitialized(IContainerProvider containerProvider)
12 {
13 var pluginMenu = containerProvider.Resolve<IPluginMenuService>();
14 pluginMenu.PluginMenus.Add(new() { Header = "プラグインA", ViewName = nameof(ViewC) });
15 }
16
17 public void RegisterTypes(IContainerRegistry containerRegistry)
18 {
19 containerRegistry.RegisterForNavigation<ViewC>();
20 }
21 }
22}
アプリケーション本体
App.xaml.cs
1using System.IO;
2using System.Reflection;
3using System;
4using System.Windows;
5using ModuleLoadManualSample.Views;
6using PluginServices;
7using PluginServices.Interfaces;
8using Prism.Ioc;
9using Prism.Modularity;
10using System.Linq;
11using ModuleB;
12using DryIoc;
13using Prism.DryIoc;
14
15namespace ModuleLoadManualSample
16{
17 /// <summary>
18 /// Interaction logic for App.xaml
19 /// </summary>
20 public partial class App
21 {
22 IContainer _container;
23
24 protected override Window CreateShell()
25 {
26 return Container.Resolve<MainWindow>();
27 }
28
29 protected override void RegisterTypes(IContainerRegistry containerRegistry)
30 {
31 containerRegistry.RegisterSingleton<IPluginMenuService, PluginMenuService>();
32
33 _container = containerRegistry.GetContainer();
34 }
35
36 protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
37 {
38 string appPath = AppContext.BaseDirectory;
39 string modulesPath = Path.Combine(appPath, "Modules");
40 string pluginsPath = Path.Combine(appPath, "Plugins");
41
42 string[] modules = Directory.GetFiles(modulesPath, "*.dll", SearchOption.TopDirectoryOnly);
43 string[] plugins = Directory.GetFiles(pluginsPath, "*.dll", SearchOption.TopDirectoryOnly);
44
45 var pluginMenuService = _container.Resolve<IPluginMenuService>();
46
47 // Modulesフォルダ以下のモジュールを追加
48 foreach (string modulePath in modules)
49 {
50 var assembly = Assembly.LoadFrom(modulePath);
51 string assemblyName = assembly.GetName().Name;
52
53 // [プロジェクト名]Moduleという命名規約にする
54 var moduleName = assembly.GetTypes().Where(x => x.Name == $"{assemblyName}Module").FirstOrDefault();
55
56 moduleCatalog.AddModule(new ModuleInfo()
57 {
58 ModuleName = moduleName.Name,
59 ModuleType = moduleName.AssemblyQualifiedName,
60 InitializationMode = InitializationMode.OnDemand
61 });
62
63 pluginMenuService.ModuleNames.Add($"{assemblyName}Module");
64 }
65
66 // Pluginsフォルダ以下のモジュールを追加
67 foreach (string modulePath in plugins)
68 {
69 var assembly = Assembly.LoadFrom(modulePath);
70 string assemblyName = assembly.GetName().Name;
71
72 var moduleName = assembly.GetTypes().Where(x => x.Name == $"{assemblyName}Module").FirstOrDefault();
73
74 moduleCatalog.AddModule(new ModuleInfo()
75 {
76 ModuleName = moduleName.Name,
77 ModuleType = moduleName.AssemblyQualifiedName,
78 InitializationMode = InitializationMode.WhenAvailable
79 });
80 }
81
82 // 参照モジュールの追加
83 moduleCatalog.AddModule<ModuleBModule>();
84 }
85 }
86}
MainWindowViewModel.cs
1using System;
2using PluginServices.Interfaces;
3using PluginServices.Interfaces.Models;
4using Prism.Modularity;
5using Prism.Mvvm;
6using Prism.Regions;
7using Reactive.Bindings;
8
9namespace ModuleLoadManualSample.ViewModels
10{
11 public class MainWindowViewModel : BindableBase
12 {
13 public ReadOnlyReactiveCollection<MenuBase> ModuleMenus { get; }
14 public ReadOnlyReactiveCollection<MenuBase> PluginMenus { get; }
15 public ReadOnlyReactiveCollection<string> ModuleNames { get; }
16
17 public ReactiveCommand<string> AddModuleCommand { get; } = new();
18 public ReactiveCommand<string> NavigateCommand { get; } = new();
19
20 public MainWindowViewModel(IRegionManager regionManager, IModuleManager moduleManager, IPluginMenuService pluginMenuService)
21 {
22 ModuleMenus = pluginMenuService.ModuleMenus.ToReadOnlyReactiveCollection();
23 PluginMenus = pluginMenuService.PluginMenus.ToReadOnlyReactiveCollection();
24 ModuleNames = pluginMenuService.ModuleNames.ToReadOnlyReactiveCollection();
25
26 AddModuleCommand.Subscribe(x =>
27 {
28 moduleManager.LoadModule(x);
29 });
30
31 NavigateCommand.Subscribe(x => regionManager.RequestNavigate("ContentRegion", x));
32 }
33 }
34}
MainWindow.xaml
1<Window x:Class="ModuleLoadManualSample.Views.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:prism="http://prismlibrary.com/"
5 xmlns:viewmodels="clr-namespace:ModuleLoadManualSample.ViewModels"
6 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7 d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel}"
8 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
9 mc:Ignorable="d"
10 prism:ViewModelLocator.AutoWireViewModel="True"
11 Title="ModuleLoadManual" Height="350" Width="525" >
12 <Grid>
13 <Grid.RowDefinitions>
14 <RowDefinition Height="Auto"/>
15 <RowDefinition/>
16 </Grid.RowDefinitions>
17 <Menu>
18 <MenuItem Header="モジュールの追加" ItemsSource="{Binding ModuleNames}">
19 <MenuItem.ItemContainerStyle>
20 <Style TargetType="MenuItem">
21 <Setter Property="Header" Value="{Binding }"/>
22 <Setter Property="Command" Value="{Binding DataContext.AddModuleCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
23 <Setter Property="CommandParameter" Value="{Binding }"/>
24 </Style>
25 </MenuItem.ItemContainerStyle>
26 </MenuItem>
27 <MenuItem Header="モジュール" ItemsSource="{Binding ModuleMenus}">
28 <MenuItem.ItemContainerStyle>
29 <Style TargetType="MenuItem">
30 <Setter Property="Header" Value="{Binding Header}"/>
31 <Setter Property="Command" Value="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
32 <Setter Property="CommandParameter" Value="{Binding ViewName}"/>
33 </Style>
34 </MenuItem.ItemContainerStyle>
35 </MenuItem>
36 <MenuItem Header="プラグイン" ItemsSource="{Binding PluginMenus}">
37 <MenuItem.ItemContainerStyle>
38 <Style TargetType="MenuItem">
39 <Setter Property="Header" Value="{Binding Header}"/>
40 <Setter Property="Command" Value="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
41 <Setter Property="CommandParameter" Value="{Binding ViewName}"/>
42 </Style>
43 </MenuItem.ItemContainerStyle>
44 </MenuItem>
45 </Menu>
46 <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
47 </Grid>
48</Window>