--- source_files: - Common/DTS.Common/Behaviors/StringMetaDataAttr.cs - Common/DTS.Common/Behaviors/TrimTextBoxBehavior.cs - Common/DTS.Common/Behaviors/InteractivityTemplate.cs - Common/DTS.Common/Behaviors/TextBoxPasteBehavior.cs - Common/DTS.Common/Behaviors/MultiSelectionBehavior.cs generated_at: "2026-04-16T02:55:26.714339+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "13307045704efc6d" --- # Behaviors ## Documentation: `DTS.Common.Behaviors` Module --- ### 1. Purpose This module provides a collection of WPF-specific behaviors and utility attributes to extend UI control functionality and metadata handling in the DTS application. It enables declarative attachment of common UI patterns—such as text trimming on `TextBox` controls, paste interception with custom command execution, and bidirectional synchronization of multi-selection state between a `ListBox` and an external `IList`—without requiring code-behind. Additionally, it offers a generic attribute (`StringMetaDataAttr`) for attaching string-based metadata to types or enum values via reflection, and a framework (`InteractivityTemplate`) for packaging and reusing collections of behaviors and triggers as reusable XAML templates. --- ### 2. Public Interface #### `StringMetaDataAttr` - **`public string MetaData { get; }`** Immutable property storing the attached metadata string. - **`internal StringMetaDataAttr(string attr)`** Constructor (internal) to initialize `MetaData`. - **`public static string GetStringMetaData(object o)`** Retrieves the `MetaData` string from a `StringMetaDataAttr` applied to the *member* (field/property) corresponding to `o.ToString()` (e.g., for an `enum` value, looks up the enum *field*). Returns `null` if no attribute found or reflection fails. #### `TrimTextBoxBehavior` - **`public class TrimTextBoxBehavior : Behavior`** Attaches to a `TextBox` and trims leading/trailing whitespace from its `Text` property on `LostFocus`. If trimming occurs, it updates the binding source. #### `InteractivityTemplate` - **`public class InteractivityTemplate : DataTemplate`** A no-op subclass of `DataTemplate`, used as a marker type for XAML-based interactivity templates. - **`public class InteractivityItems : FrameworkElement`** Holds collections of `Behavior`s and `TriggerBase`s. Exposes: - **`public List Triggers { get; }`** Lazy-initialized list for triggers. - **`public List Behaviors { get; }`** Lazy-initialized list for behaviors. - **`public static readonly DependencyProperty TemplateProperty`** Attached dependency property `"Template"` of type `InteractivityTemplate`. - **`public static InteractivityTemplate GetTemplate(DependencyObject obj)` / `SetTemplate(...)`** Accessors for the `Template` attached property. When set, loads the `InteractivityTemplate`’s content (expected to be an `InteractivityItems` instance), and copies its `Behaviors` and `Triggers` into the target object via `Interaction.GetBehaviors(obj)` and `Interaction.GetTriggers(obj)`. #### `TextBoxPasteBehavior` - **`public static readonly DependencyProperty PasteCommandProperty`** Attached dependency property for `PasteCommand`. - **`public static ICommand GetPasteCommand(DependencyObject target)` / `SetPasteCommand(...)`** Get/set the `PasteCommand` attached property. - **Behavior**: When `PasteCommand` is set on a `TextBox`, `ChannelCodeBuilder`, or `ChannelNameBuilder` (using their `MainEditBox`), it: - Subscribes to `CommandManager.ExecutedEvent` for `Paste` commands. - On paste execution, retrieves clipboard text (though *not used* in current implementation). - Invokes `PasteCommand.Execute(textBox)` if `CanExecute(null)` returns `true`. - Sets `e.Handled = true` to suppress default paste. - On error, publishes a `PageErrorEvent` via Prism’s `IEventAggregator`. #### `MultiSelectionBehavior` - **`public class MultiSelectionBehavior : Behavior`** Synchronizes selection state between a `ListBox.SelectedItems` and an external `IList` (typically `ObservableCollection` or `BulkObservableCollection`). - **`public IList SelectedItems { get; set; }`** Dependency property backing the external list to sync with. - **Key behaviors**: - On attach: Populates `ListBox.SelectedItems` from `SelectedItems`. - On `SelectedItems` property change: - Clears `ListBox.SelectedItems`, repopulates from new value. - Subscribes to `CollectionChanged` on the new value and `SelectionChanged` on the `ListBox`. - On `ListBox.SelectionChanged`: Updates `SelectedItems` (external list) with added/removed items. - Uses `SelectedItemsStatus.SetUpdating(...)` to suppress notifications during bulk updates. - Special handling for `BulkObservableCollection` and `BulkObservableCollection` via `BulkOperations*` methods to use `AddRange`/`RemoveRange` for efficiency. - On `SelectedItems.CollectionChanged`: Updates `ListBox.SelectedItems`. --- ### 3. Invariants - **`StringMetaDataAttr.GetStringMetaData(object o)`** - Only inspects the *member* named by `o.ToString()` (e.g., for `MyEnum.ValueX`, looks for `MyEnum.ValueX` field/property). - Returns `null` if `o` is `null`, member lookup fails, or attribute is absent. - Does *not* inspect attributes on the *type* of `o`, only its *member*. - **`TrimTextBoxBehavior`** - Only trims on `LostFocus`. Does *not* trim on input, typing, or `GotFocus`. - Only updates the binding source if the trimmed text differs from current text. - **`InteractivityItems.Template`** - The `InteractivityTemplate.LoadContent()` result *must* be an `InteractivityItems` instance (or compatible), otherwise casting fails. - Behavior/trigger addition is *additive*—does not clear existing behaviors/triggers on the target object. - **`TextBoxPasteBehavior`** - Only handles `ApplicationCommands.Paste` or a `RoutedUICommand` named `"Paste"`. - Does *not* inspect or modify clipboard content—only invokes the bound `PasteCommand` with the `TextBox` as parameter. - Relies on `ContainerLocator.Container` being initialized (for `IEventAggregator` resolution); failure here may cause silent logging errors. - **`MultiSelectionBehavior`** - Requires `SelectedItems` to be an `IList` (not `IEnumerable`) and ideally `INotifyCollectionChanged` for two-way sync. - Uses `SelectedItemsStatus.SetUpdating(...)` to signal bulk operations—consumers *must* respect this flag to avoid spurious notifications. - Type-specific bulk operations (`BulkOperations*`) only apply when `SelectedItems` is a `BulkObservableCollection` with `T` being `IAnalogSensor` or `ITestSetup`. - `AddItems` and `RemoveItems` methods perform null checks; exceptions during item addition are logged via `APILogger.Log(ex)` but do *not* halt processing. --- ### 4. Dependencies #### Dependencies *of* this module: - **WPF**: `System.Windows`, `System.Windows.Controls`, `System.Windows.Input`, `System.Windows.Interactivity` (via `Microsoft.Xaml.Behaviors`). - **Prism**: `Prism.Ioc`, `Prism.Events` (`IEventAggregator`, `ContainerLocator`). - **Domain types**: - `DTS.Common.Controls.ChannelCodeBuilder`, `ChannelNameBuilder` (for `MainEditBox`). - `DTS.Common.Interface.Sensors.SensorsList.IAnalogSensor`, `ITestSetup`. - `DTS.Common.Classes.BulkObservableCollection`. - `DTS.Common.Enums`, `DTS.Common.Utilities.Logging.APILogger`, `SelectedItemsStatus`. #### Dependencies *on* this module: - Likely used by UI layers (e.g., XAML views) to attach behaviors declaratively. - `InteractivityItems.Template` is intended for use in XAML `DataTemplate`s to share interactivity logic across controls. --- ### 5. Gotchas - **`StringMetaDataAttr.GetStringMetaData`** - **Misleading name**: Operates on *members* (e.g., enum fields), not the *type* of the object passed. Passing an enum *value* works, but passing a non-enum object (e.g., `new MyClass()`) will likely fail unless `o.ToString()` matches a member name. - Uses `GetMember(o.ToString())`, which is case-sensitive and may fail if `ToString()` is overridden unexpectedly. - **`TrimTextBoxBehavior`** - Does *not* handle `TextChanged` or validation—trimming happens only on `LostFocus`, which may cause UX surprises if binding updates are deferred. - **`InteractivityItems.Template`** - `OnTemplateChanged` assumes `LoadContent()` returns an `InteractivityItems` instance. If the XAML template is malformed or returns a different type, a runtime cast exception occurs. - **`TextBoxPasteBehavior`** - Clipboard text is retrieved (`Clipboard.GetText()`) but *never used*—the `PasteCommand` receives the `TextBox` as parameter, but the command implementation must handle clipboard content itself. - Uses `ContainerLocator.Container` (static), which may cause issues in test scenarios or if Prism container initialization is delayed. - **`MultiSelectionBehavior`** - **Critical**: The `SelectedItems` list *must* be mutable and support `Add`/`Remove`/`Clear`. If it’s read-only (e.g., `IList` wrapping an array), runtime exceptions occur. - Bulk operations (`BulkOperations*`) only trigger for *exact* type matches: `BulkObservableCollection` or `BulkObservableCollection`. Other generic instantiations (e.g., `BulkObservableCollection`) fall back to per-item operations. - `SelectedItemsStatus.SetUpdating(...)` is used internally, but its implementation (`SelectedItemsStatus` class) is *not* provided in the source—consumers must ensure it’s correctly implemented to avoid infinite loops or missed updates. - **General**: - All behaviors assume WPF’s `Interaction` service is available (via `Microsoft.Xaml.Behaviors`). - No explicit disposal/cleanup in `MultiSelectionBehavior` for event handlers beyond `OnDetaching`—re-attaching may cause duplicate subscriptions if not handled carefully.