Files
2026-04-17 14:55:32 -04:00

10 KiB
Raw Permalink Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.Common/RibbonControl/RibbonControlSelectionChanged.cs
Common/DTS.Common/RibbonControl/RibbonControlOperation.cs
Common/DTS.Common/RibbonControl/RibbonControlSelectionEventArgs.cs
Common/DTS.Common/RibbonControl/RibbonControlSelectionChangeBehavior.cs
Common/DTS.Common/RibbonControl/RibbonRegionAdapter.cs
2026-04-16T02:56:16.038197+00:00 Qwen/Qwen3-Coder-Next-FP8 1 bf8430cdc3eeb064

RibbonControl

Documentation: Ribbon Control Selection Module


1. Purpose

This module provides infrastructure for integrating Prism-based region management with the Microsoft Ribbon control (Microsoft.Windows.Controls.Ribbon.Ribbon) and for publishing selection-change events when ribbon tabs are added, removed, or selected. It enables decoupled communication between ribbon UI components and view models via Prisms event aggregation system. Specifically, it adapts the Ribbon control to Prism regions, manages the addition/removal of RibbonTab and RibbonApplicationMenu items, and publishes RibbonControlSelectionChanged events whenever tab selection changes (e.g., due to user interaction or programmatic activation). This supports view synchronization and state management in a modular WPF application.


2. Public Interface

RibbonControlSelectionChanged

  • Type: class inheriting from PubSubEvent<RibbonControlSelectionEventArgs>
  • Behavior: A Prism PubSubEvent used to broadcast tab selection changes. Subscribers receive RibbonControlSelectionEventArgs instances indicating the operation type (AddedItem or RemovedItem) and the affected item.

RibbonControlOperation

  • Type: enum
  • Values:
    • AddedItem: Indicates an item was added to the ribbon (e.g., a tab).
    • RemovedItem: Indicates an item was removed from the ribbon.

RibbonControlSelectionEventArgs

  • Constructor:
    RibbonControlSelectionEventArgs(RibbonControlOperation operation, object item)
    • Parameters:
      • operation: The operation type (AddedItem or RemovedItem).
      • item: The ribbon item (typically a RibbonTab) involved in the operation.
  • Properties:
    • Operation: Gets the operation type.
    • Item: Gets the added/removed item (strongly typed as object; callers must cast appropriately).

RibbonControlSelectionChangeBehavior

  • Type: class inheriting from Behavior<Ribbon>
  • Properties:
    • TargetRibbonControl: Ribbon dependency property (note: backing field name is TargetRibbonProperty, but property name is TargetRibbonControl).
  • Methods:
    • OnAttached(): Subscribes to Ribbon.SelectionChanged; initializes _eventAggregator via ContainerLocator.Container.
    • OnDetaching(): Unsubscribes from Ribbon.SelectionChanged.
    • Ribbon_SelectionChanged(object sender, SelectionChangedEventArgs e):
      • Publishes two events per SelectionChangedEventArgs if both AddedItems and RemovedItems are non-empty:
        • One RibbonControlSelectionChanged event for AddedItem with e.AddedItems[0].
        • One RibbonControlSelectionChanged event for RemovedItem with e.RemovedItems[0].
      • Only the first added/removed item is used (index 0), ignoring additional items.

RibbonRegionAdapter

  • Type: class inheriting from RegionAdapterBase<Ribbon>, implements IDisposable
  • Constructor:
    RibbonRegionAdapter(IRegionBehaviorFactory, IRegionManager, IEventAggregator)
    • Subscribes to RibbonControlSelectionChanged event (note: uses TabControlSelectionChanged, which is a typo—see Gotchas).
  • Methods:
    • Adapt(IRegion region, Ribbon regionTarget):
      • Hooks region.ActiveViews.CollectionChanged to add/remove RibbonTab/RibbonApplicationMenu items to/from regionTarget.Items or regionTarget.ApplicationMenu.
      • Assigns a Uid (if missing) to new tabs using Guid.NewGuid().GetHashCode().ToString(...).
      • Applies SortDescription("TabIndex", Ascending) to regionTarget.Items.SortDescriptions.
      • Sets IsSelected on a newly added tab if its header matches AppSettings["DefaultRibbonTab"] (case-insensitive).
    • AddRibbonTabToRegion(RibbonTab, Ribbon): Adds tab to Items, sets Uid, sorts, and optionally selects.
    • AddApplicationMenuToRegion(RibbonApplicationMenu, Ribbon): Assigns to regionTarget.ApplicationMenu.
    • RemoveRibbonTabFromRibbonRegion(RibbonTab, Ribbon): Removes tab from Items.
    • OnTabControlSelectionChanged(TabControlSelectionEventArgs):
      • Only processes AddedItem operations (ignores RemovedItem).
      • Attempts to synchronize tab selection based on IRibbonTabInfoProvider.RibbonTabUid from the views DataContext.
      • If the selected tabs UID does not match the views expected tab UID, it programmatically selects the matching tab.
    • CreateRegion(): Returns new AllActiveRegion() (all views remain active).
  • IDisposable:
    • Dispose(bool): Unsubscribes from RibbonControlSelectionChanged.
    • Finalizer (~RibbonRegionAdapter) calls Dispose(false).

3. Invariants

  • Event publishing:
    • RibbonControlSelectionChanged is published only for the first item in AddedItems and RemovedItems (index 0), even if multiple items change simultaneously.
  • Operation semantics:
    • RibbonControlOperation.AddedItem corresponds to items in SelectionChangedEventArgs.AddedItems.
    • RibbonControlOperation.RemovedItem corresponds to items in SelectionChangedEventArgs.RemovedItems.
  • UID requirement:
    • RibbonTab.Uid must be set (either manually or auto-generated) for RibbonRegionAdapter.OnTabControlSelectionChanged to function correctly.
  • View contract:
    • For OnTabControlSelectionChanged to synchronize selection, the views DataContext must implement IRibbonTabInfoProvider and return a non-empty RibbonTabUid.
  • Thread safety:
    • AddRibbonTabToRegion uses a lock (Lock) around Uid assignment and Items.Add, but RemoveRibbonTabFromRibbonRegion does not. This may lead to race conditions during concurrent add/remove operations.

4. Dependencies

Internal Dependencies (from source):

  • Prism libraries:
    • Prism.Events (PubSubEvent, IEventAggregator)
    • Prism.Regions (RegionAdapterBase, IRegion, IRegionManager)
    • Prism.Ioc (ContainerLocator)
  • WPF & Ribbon:
    • System.Windows (DependencyProperty, SelectionChangedEventArgs)
    • System.Windows.Controls (Ribbon, RibbonTab, RibbonApplicationMenu)
    • Microsoft.Windows.Controls.Ribbon ( RibbonControlsLibrary)
    • Microsoft.Xaml.Behaviors (Behavior<T>)
  • Common project types:
    • DTS.Common.Classes.IBaseView
    • DTS.Common.Enums.IRibbonTabInfoProvider
    • DTS.Common.Events.TabControlSelectionChanged (note: typo in usage—see Gotchas)

External Dependencies:

  • RibbonControlsLibrary NuGet package (required at runtime).
  • App.config with optional "DefaultRibbonTab" key.

Dependencies on this module:

  • RibbonRegionAdapter is used to adapt Ribbon controls in Prism regions (e.g., RegionNames.RibbonRegion).
  • RibbonControlSelectionChanged event is consumed by RibbonRegionAdapter and likely by other modules/view models to react to tab changes.

5. Gotchas

  • Typo in event name:

    • RibbonRegionAdapter subscribes to TabControlSelectionChanged (line: _eventAggregator.GetEvent<TabControlSelectionChanged>()), but the defined event is named RibbonControlSelectionChanged. This will cause a runtime EventNotFoundException unless TabControlSelectionChanged is defined elsewhere (not present in source).
    • Likely bug: Should be RibbonControlSelectionChanged.
  • Event semantics mismatch:

    • RibbonControlSelectionChangedEventArgs.Operation is named RibbonControlOperation, but RibbonRegionAdapter.OnTabControlSelectionChanged checks for TabControlOperation.RemovedItem (again, typo—should be RibbonControlOperation).
    • The Operation property in RibbonControlSelectionEventArgs is of type RibbonControlOperation, but the handler in RibbonRegionAdapter references TabControlOperation (undefined in source). This will not compile unless TabControlOperation exists elsewhere.
  • Only first item processed:

    • RibbonControlSelectionChangeBehavior.Ribbon_SelectionChanged publishes events only for e.AddedItems[0] and e.RemovedItems[0]. If multiple tabs are selected/removed in one operation (e.g., via SelectedItems manipulation), only the first is handled.
  • UID auto-generation side effect:

    • AddRibbonTabToRegion assigns a new Uid every time a tab is added if Uid is null/empty. This may cause issues if Uid is used for persistence or tracking across sessions.
  • Race condition in RemoveRibbonTabFromRibbonRegion:

    • Removal is not synchronized (unlike addition), risking InvalidOperationException if concurrent modifications occur.
  • Selection logic assumes single active tab:

    • OnTabControlSelectionChanged selects a tab only if currentTab == null or ribbonTabInfo.RibbonTabUid != currentTab.Uid, but does not handle cases where multiple tabs might be active (though AllActiveRegion is used, selection is still single-tab in Ribbon control).
  • No validation of Item type:

    • RibbonControlSelectionEventArgs.Item is object. Consumers must cast to RibbonTab or RibbonApplicationMenu safely.
  • TargetRibbonControl property name mismatch:

    • The DependencyProperty is registered as "TargetRibbonControl", but the backing field is TargetRibbonProperty. This is valid, but the naming is inconsistent (TargetRibbonControl vs TargetRibbonProperty).
  • RibbonRegionAdapter subscribes to event in constructor:

    • If the adapter is created before IEventAggregator is fully initialized, subscription may fail. Not an issue if DI container ensures ordering, but a risk in complex setups.
  • RibbonControlSelectionChangeBehavior does not validate AssociatedObject:

    • Assumes AssociatedObject is non-null and implements Ribbon (enforced by Behavior<Ribbon>), but SelectionChanged handler does not check sender type.

None identified beyond the above.