Files
DP44/GLM5Analysis/PatternLibrary/PrismModulePattern.md

188 lines
6.1 KiB
Markdown
Raw Normal View History

2026-04-17 14:55:32 -04:00
# 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<I{ViewName}, {ViewName}>();
_unityContainer.RegisterType<I{ViewModelName}, {ViewModelName}>();
}
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<ISensorsListView, SensorsListView>();
_unityContainer.RegisterType<ISensorsListViewModel, SensorsListViewModel>();
}
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<IGraphView, GraphView>();
_unityContainer.RegisterType<IGraphViewModel, GraphViewModel>();
}
}
```
### Example 3: Bootstrapper Registration
**File:** `DataPRO/DataPRO/Bootstrapper.cs:179-220`
```csharp
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<StatusAndProgressBar.StatusAndProgressBarModule>();
moduleCatalog.AddModule<DatabaseServices.DatabaseServicesModule>();
moduleCatalog.AddModule<SensorsList.SensorsListModule>();
// ... 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