Files

152 lines
8.6 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
source_files:
- Common/DTS.CommonCore/RibbonControl/RibbonControlSelectionChanged.cs
- Common/DTS.CommonCore/RibbonControl/RibbonControlOperation.cs
- Common/DTS.CommonCore/RibbonControl/RibbonControlSelectionEventArgs.cs
- Common/DTS.CommonCore/RibbonControl/RibbonControlSelectionChangeBehavior.cs
- Common/DTS.CommonCore/RibbonControl/RibbonRegionAdapter.cs
generated_at: "2026-04-16T02:16:32.989533+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "c0e1c8bd7f1a18f4"
---
# RibbonControl
## Documentation: `DTS.Common.RibbonControl` Module
---
### 1. Purpose
This module provides infrastructure for integrating the Microsoft Ribbon control (from the *RibbonControlsLibrary*) into a Prism-based WPF application, specifically enabling Prism region management and event-driven tab selection coordination for `Ribbon` and `RibbonTab` elements. It defines event types for tab selection changes, a behavior to bridge `Ribbon.SelectionChanged` to Prism events, and a region adapter to manage `RibbonTab` and `RibbonApplicationMenu` views within the `RibbonRegion`. Its role is to decouple view navigation logic from UI controls while preserving Prisms region-based architecture and enabling reactive tab selection behavior based on view model metadata.
---
### 2. Public Interface
#### `RibbonControlSelectionChanged`
- **Type**: `class` (inherits `CompositePresentationEvent<RibbonControlSelectionEventArgs>`)
- **Purpose**: Prism event used to publish tab selection changes (add/remove operations) on the `Ribbon`.
- **Usage**: Subscribers listen for tab operations via `IEventAggregator.GetEvent<RibbonControlSelectionChanged>().Subscribe(...)`.
- **Payload**: `RibbonControlSelectionEventArgs`
#### `RibbonControlOperation`
- **Type**: `enum`
- **Values**:
- `AddedItem`: Indicates a tab was added to the `Ribbon`.
- `RemovedItem`: Indicates a tab was removed from the `Ribbon`.
#### `RibbonControlSelectionEventArgs`
- **Type**: `class`
- **Properties**:
- `Operation`: `RibbonControlOperation` — the type of operation (`AddedItem` or `RemovedItem`).
- `Item`: `object` — the `RibbonTab` (or other item) involved in the operation.
- **Constructor**:
`RibbonControlSelectionEventArgs(RibbonControlOperation operation, object item)`
Initializes the event args with the operation and item.
#### `RibbonControlSelectionChangeBehavior`
- **Type**: `class` (inherits `Behavior<Ribbon>`)
- **Properties**:
- `TargetRibbonControl`: `Ribbon` (dependency property) — *Note: Property name mismatch in XAML usage; registered as `"TargetRibbonControl"` but exposed as `TargetRibbonControl`.*
- **Behavior**:
- Attaches to `Ribbon.SelectionChanged` event.
- On selection change, publishes **two** events (one for `AddedItem`, one for `RemovedItem`) via `RibbonControlSelectionChanged`, using the first item in `e.AddedItems` and `e.RemovedItems` respectively.
- Uses `ServiceLocator.Current.GetInstance<IEventAggregator>()` to publish events.
#### `RibbonRegionAdapter`
- **Type**: `class` (inherits `RegionAdapterBase<Ribbon>`, implements `IDisposable`)
- **Constructor**:
```csharp
RibbonRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory, IRegionManager regionManager, IEventAggregator eventAggregator)
```
- Registers for `RibbonControlSelectionChanged` events via `_eventAggregator.GetEvent<TabControlSelectionChanged>().Subscribe(OnTabControlSelectionChanged)`.
⚠️ **Note**: Event name mismatch — uses `TabControlSelectionChanged` (see *Gotchas*).
- **Methods**:
- `Adapt(IRegion region, Ribbon regionTarget)`:
Subscribes to `region.ActiveViews.CollectionChanged`, handling `RibbonTab` and `RibbonApplicationMenu` additions/removals:
- Adds `RibbonTab` to `regionTarget.Items`, assigns a `Uid` if missing, sorts by `TabIndex`.
- Adds `RibbonApplicationMenu` to `regionTarget.ApplicationMenu`.
- Sets `IsSelected = true` on the first added tab if `App.config` contains `<add key="DefaultRibbonTab" value="TabHeaderName"/>`.
- `AddRibbonTabToRegion(RibbonTab, Ribbon)`: Internal helper.
- `AddApplicationMenuToRegion(RibbonApplicationMenu, Ribbon)`: Internal helper.
- `RemoveRibbonTabFromRibbonRegion(RibbonTab, Ribbon)`: Removes tab from `Ribbon.Items`.
- `OnTabControlSelectionChanged(TabControlSelectionEventArgs)`:
Reacts to tab selection changes:
- Ignores `RemovedItem` operations.
- Extracts `IBaseView``IRibbonTabInfoProvider` from views `DataContext`.
- Uses `RibbonTabUid` to programmatically select the corresponding `RibbonTab` in the region.
- `CreateRegion()`: Returns `new AllActiveRegion()` (all views remain active).
- **IDisposable**:
- Unsubscribes from `RibbonControlSelectionChanged` in `Dispose(bool)`.
---
### 3. Invariants
- **Event naming consistency**:
`RibbonControlSelectionChanged` (defined in `RibbonControlSelectionChanged.cs`) is *not* the same as `TabControlSelectionChanged` (used in `RibbonRegionAdapter.cs`). This is a critical mismatch (see *Gotchas*).
- **Item identity**:
`RibbonControlSelectionEventArgs.Item` is always the *first* item in `AddedItems` or `RemovedItems` — only single-item operations are handled.
- **Tab selection logic**:
`RibbonRegionAdapter.OnTabControlSelectionChanged` only acts on `AddedItem` operations and only if:
- `e.Item` is an `IBaseView`,
- its `DataContext` implements `IRibbonTabInfoProvider`,
- `IRibbonTabInfoProvider.RibbonTabUid` is non-null/non-empty,
- and the `RibbonTab` with matching `Uid` exists in the region.
- **UID assignment**:
`RibbonTab.Uid` and `RibbonApplicationMenu.Uid` are auto-generated (via `Guid`) if missing during `Adapt()`.
---
### 4. Dependencies
#### Dependencies *of* this module:
- **Prism libraries**:
`Microsoft.Practices.Prism.Events`, `Microsoft.Practices.Prism.Regions`, `Microsoft.Practices.ServiceLocation`.
- **WPF & Ribbon**:
`System.Windows`, `System.Windows.Controls`, `Microsoft.Windows.Controls.Ribbon` (from *RibbonControlsLibrary*).
- **Custom types**:
- `DTS.Common.Classes.IBaseView`
- `DTS.Common.Enums.IRibbonTabInfoProvider`
- `DTS.Common.Events.TabControlSelectionChanged` *(note: event name mismatch)*
- `DTS.Common.Base.RegionNames.RibbonRegion`
#### Dependencies *on* this module:
- Application code that uses Prism regions (`RegionNames.RibbonRegion`) to host `Ribbon` controls.
- View models implementing `IRibbonTabInfoProvider` to enable programmatic tab selection.
- Behaviors or services that subscribe to `RibbonControlSelectionChanged` for tab lifecycle monitoring.
---
### 5. Gotchas
- **Event name mismatch**:
`RibbonRegionAdapter` subscribes to `TabControlSelectionChanged`, but the event defined in this module is `RibbonControlSelectionChanged`. This will cause `OnTabControlSelectionChanged` to *never fire*, breaking tab selection coordination.
**Fix required**: Change `TabControlSelectionChanged` to `RibbonControlSelectionChanged` in `RibbonRegionAdapter` constructor and `Dispose`.
- **Behavior uses `ServiceLocator`**:
`RibbonControlSelectionChangeBehavior` uses `ServiceLocator.Current.GetInstance<IEventAggregator>()` instead of constructor injection. This violates DI best practices and makes testing harder.
- **Single-item assumption**:
Only the *first* added/removed item is used (`e.AddedItems[0]`, `e.RemovedItems[0]`). Multi-select scenarios (if supported by `Ribbon`) will be silently ignored.
- **Property name mismatch**:
The dependency property is registered as `"TargetRibbonControl"` but exposed as `TargetRibbonControl`. While syntactically valid, this may cause confusion in XAML binding (e.g., `TargetRibbonControl="{Binding ...}"` is required — not `TargetRibbon`).
- **Hardcoded config key**:
`DefaultRibbonTab` is read from `App.config` without validation or fallback. If the header string doesnt match exactly (case-insensitive), selection wont occur.
- **No null safety for `Item`**:
`RibbonControlSelectionEventArgs.Item` is `object`, but downstream logic (e.g., `OnTabControlSelectionChanged`) assumes it is an `IBaseView`. Casting failures will be silent (via `as` operator).
- **Thread-safety via `lock`**:
`AddRibbonTabToRegion` uses a `static readonly lock`, but `RibbonRegionAdapter` is instantiated per region (via DI), so the lock is shared across *all* ribbon regions — potentially causing contention or deadlocks if multiple regions are initialized concurrently.
- **Missing `RibbonControlOperation` usage**:
`RibbonControlOperation` is defined but only used in `RibbonControlSelectionEventArgs`. No other code references it directly.
---
*Documentation generated from provided source files. No external behavior or APIs inferred.*