# Prism Module Pattern ## When to Use - Creating a new feature module that integrates with DataPRO shell - Adding new UI components that need to be loaded at runtime - Implementing isolated feature sets that can be developed independently ## Files to Create/Modify ### 1. Module Project Structure ``` DataPRO/Modules/{ModuleName}/ ├── {ModuleName}Module.cs (Module entry point) ├── View/ │ └── {ViewName}.xaml(.cs) (XAML views) ├── ViewModel/ │ └── {ViewModelName}.cs (View models) ├── Model/ (Data models, if needed) └── Resources/ (String resources, images) ``` ### 2. Register Module in Bootstrapper **File:** `DataPRO/DataPRO/Bootstrapper.cs` Add to `ConfigureModuleCatalog()`: ```csharp moduleCatalog.AddModule<{ModuleName}.{ModuleClass}>(); ``` ## Code Template ### Module Class (`{ModuleName}Module.cs`) ```csharp using System; using System.ComponentModel.Composition; using System.Windows.Media.Imaging; using DTS.Common; using DTS.Common.Interface; using Prism.Ioc; using Prism.Modularity; using Unity; [assembly: {ModuleName}Name] [assembly: {ModuleName}ImageAttribute] namespace {ModuleName} { [Export(typeof(IModule))] [Module(ModuleName = "{ModuleName}")] public class {ModuleName}Module : IModule { private readonly IUnityContainer _unityContainer; public {ModuleName}Module(IUnityContainer unityContainer) { _unityContainer = unityContainer; } public void Initialize() { _unityContainer.RegisterType(); _unityContainer.RegisterType(); } public void OnInitialized(IContainerProvider containerProvider) { } public void RegisterTypes(IContainerRegistry containerRegistry) { Initialize(); } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] public class {ModuleName}NameAttribute : TextAttribute { public {ModuleName}NameAttribute() : this(null) { } public {ModuleName}NameAttribute(string s) { AssemblyName = AssemblyNames.{ModuleName}.ToString(); } public override string AssemblyName { get; } public override Type GetAttributeType() => typeof(TextAttribute); public override string GetAssemblyName() => AssemblyName; } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] public class {ModuleName}ImageAttribute : ImageAttribute { private BitmapImage _img; public {ModuleName}ImageAttribute() : this(null) { } public override BitmapImage AssemblyImage { get { _img = AssemblyInfo.GetImage(AssemblyNames.{ModuleName}.ToString()); return _img; } } public {ModuleName}ImageAttribute(string s) { _img = AssemblyInfo.GetImage(AssemblyNames.{ModuleName}.ToString()); } public override Type GetAttributeType() => typeof(ImageAttribute); public override BitmapImage GetAssemblyImage() => AssemblyImage; private string _name; public override string AssemblyName { get { _name = AssemblyNames.{ModuleName}.ToString(); return _name; } } public override string GetAssemblyName() => AssemblyName; private string _group; public override string AssemblyGroup { get { _group = eAssemblyGroups.{GroupType}.ToString(); return _group; } } public override string GetAssemblyGroup() => AssemblyGroup; private eAssemblyRegion _region; public override eAssemblyRegion AssemblyRegion { get { _region = eAssemblyRegion.{ModuleName}Region; return _region; } } public override eAssemblyRegion GetAssemblyRegion() => AssemblyRegion; } } ``` ## Examples from Codebase ### Example 1: SensorsListModule **File:** `DataPRO/Modules/SensorsList/SensorsList/SensorsListModule.cs:22` ```csharp [Module(ModuleName = "SensorsListModule")] public class SensorsListModule : IModule { private readonly IUnityContainer _unityContainer; public SensorsListModule(IUnityContainer unityContainer) { _unityContainer = unityContainer; } public void Initialize() { _unityContainer.RegisterType(); _unityContainer.RegisterType(); } public void RegisterTypes(IContainerRegistry containerRegistry) => Initialize(); } ``` ### Example 2: GraphModule (DTS Viewer) **File:** `DTS Viewer/DTS.Viewer.Modules/DTS.Viewer.Graph/GraphModule.cs:16` ```csharp [Module(ModuleName = "Graph")] public class GraphModule : IModule { private readonly IUnityContainer _unityContainer; public GraphModule(IUnityContainer unityContainer) { _unityContainer = unityContainer; } public void Initialize() { _unityContainer.RegisterType(); _unityContainer.RegisterType(); } } ``` ### Example 3: Bootstrapper Registration **File:** `DataPRO/DataPRO/Bootstrapper.cs:179-220` ```csharp protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule(); moduleCatalog.AddModule(); moduleCatalog.AddModule(); // ... more modules } ``` ## Common Mistakes to Avoid 1. **Forgetting to register module in Bootstrapper** - Module won't load 2. **Not implementing both `Initialize()` and `RegisterTypes()`** - Both are required for Prism 6+ 3. **Missing assembly attributes** - Module won't appear in UI tiles/menu 4. **Wrong eAssemblyGroups value** - Module appears in wrong section 5. **Not using interface for View/ViewModel registration** - Breaks testability and DI 6. **Singleton vs Transient** - Use `ContainerControlledLifetimeManager` for singletons, otherwise default is transient 7. **Missing `[Export(typeof(IModule))]`** - Required for MEF discovery in some scenarios