Files
DP44/enriched-qwen3-coder-next/Common/DTS.CommonCore/RibbonControl.md
2026-04-17 14:55:32 -04:00

152 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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.*