Files
DP44/enriched-qwen3-coder-next/Common/DTS.Common/Behaviors.md

152 lines
9.8 KiB
Markdown
Raw Normal View History

2026-04-17 14:55:32 -04:00
---
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<TextBox>`**
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<TriggerBase> Triggers { get; }`**
Lazy-initialized list for triggers.
- **`public List<Behavior> 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 Prisms `IEventAggregator`.
#### `MultiSelectionBehavior`
- **`public class MultiSelectionBehavior : Behavior<ListBox>`**
Synchronizes selection state between a `ListBox.SelectedItems` and an external `IList` (typically `ObservableCollection<T>` or `BulkObservableCollection<T>`).
- **`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<IAnalogSensor>` and `BulkObservableCollection<ITestSetup>` 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<T>` 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<T>`.
- `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 its read-only (e.g., `IList` wrapping an array), runtime exceptions occur.
- Bulk operations (`BulkOperations*`) only trigger for *exact* type matches: `BulkObservableCollection<IAnalogSensor>` or `BulkObservableCollection<ITestSetup>`. Other generic instantiations (e.g., `BulkObservableCollection<DerivedSensor>`) 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 its correctly implemented to avoid infinite loops or missed updates.
- **General**:
- All behaviors assume WPFs `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.