152 lines
9.8 KiB
Markdown
152 lines
9.8 KiB
Markdown
|
|
---
|
|||
|
|
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 Prism’s `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 it’s 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 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.
|