Files

101 lines
6.6 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
source_files:
- DataPRO/Modules/ISO/ExtraProperties/Model/ExtraPropertyModel.cs
generated_at: "2026-04-16T04:39:00.347162+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "f8c9e5793048774d"
---
# Model
### **Purpose**
This module implements the `ExtraPropertyModel` class, a concrete data model for representing key-value pairs (e.g., ISO channel codes and names or user-defined properties) within a WPF-based UI, conforming to the `IExtraProperty` interface. It supports property change notifications via Prisms `BasePropertyChanged`, integrates with Prisms event aggregation system for cross-module communication (e.g., signaling page modifications or paste events), and provides a custom `PasteCommand` to handle clipboard paste operations with special handling for multi-line or multi-field text. Its role is to serve as the view-model backing for UI controls that edit extra properties in ISO data contexts.
---
### **Public Interface**
#### **`ExtraPropertyModel` class**
- **`public ExtraPropertyModel(IExtraProperty extraProperty)`**
Initializes a new instance by copying `Key` and `Value` from an existing `IExtraProperty` instance. Registers paste commands during construction.
- **`public ExtraPropertyModel()`**
Default constructor. Registers paste commands. `Key` and `Value` are initialized to `null`.
- **`public ExtraPropertyModel(KeyValuePair<string, string> extraProperty)`**
Initializes a new instance using a `KeyValuePair<string, string>`. Registers paste commands.
- **`public UIItemStatus ItemStatus { get; set; }`**
Gets or sets the UI status of the item (e.g., `None`, `Added`, `Modified`, `Deleted`). Backed by `SetProperty` for change notifications.
- **`public string Key { get; set; }`**
Gets or sets the code (e.g., ISO code or user code). Backed by `SetProperty`.
- **`public string Value { get; set; }`**
Gets or sets the associated name (e.g., ISO channel name or user channel name). Backed by `SetProperty`.
- **`public ICommand PasteCommand { get; set; }`**
Exposes the command bound to paste operations. Initialized to a `PasteCommandClass` instance with `PASTE_ID = "ExtraProperty"`.
- **`public const string PASTE_ID = "ExtraProperty"`**
Constant identifier used to distinguish paste operations originating from this model.
#### **`PasteCommandClass` class**
- **`public string Id { get; }`**
Gets the command ID passed during construction (e.g., `"ExtraProperty"`).
- **`public bool CanExecute(object parameter)`**
Always returns `true`.
- **`public void Execute(object parameter)`**
Handles paste logic:
- Validates `parameter` is a `TextBox`.
- Attempts to resolve `IExtraProperty` from `TextBox.DataContext`, falling back to `ChannelCodeBuilder` or `ChannelNameBuilder` contexts.
- Checks clipboard contains text.
- If clipboard text has exactly one line and contains no delimiters (`','`, `';'`, `'\t'`), publishes `PageModifiedEvent` and exits early (single-field paste is handled by `TextChanged`).
- Otherwise, clears `Key`/`Value` on the target `IExtraProperty` (a no-op assignment to trigger change tracking), then publishes `TextPastedEvent` with the raw text, target property, command ID, and `TextBox.Tag`.
- On exception, publishes `PageErrorEvent` with the exception message.
- **`public event EventHandler CanExecuteChanged`**
Required by `ICommand`, but never raised (no-op).
- **`public PasteCommandClass(string id)`**
Constructor storing `id` in `Id`.
---
### **Invariants**
- `Key` and `Value` may be `null`; no validation is enforced on their values.
- `ItemStatus` defaults to `UIItemStatus.None` and must be explicitly set by consumers.
- The `PasteCommand` is registered globally via `CommandManager.RegisterClassCommandBinding`, meaning it applies to *all* instances of `ExtraPropertyModel` and its subclasses.
- The `Paste` method (private) is never implemented — it is a stub. Actual paste behavior resides entirely in `PasteCommandClass.Execute`.
- `CanExecuteChanged` is never raised by `PasteCommandClass`, so WPF command bindings may not update UI state (e.g., button enablement) dynamically.
---
### **Dependencies**
- **Imports/Usings**:
- `DTS.Common.ISO.ExtraProperties` → Provides `IExtraProperty` interface.
- `DTS.Common.Controls` → Provides `ChannelCodeBuilder`, `ChannelNameBuilder` types.
- `DTS.Common.Enums` → Provides `UIItemStatus`.
- `DTS.Common.Events` → Provides `PageModifiedEvent`, `TextPastedEvent`, `PageErrorEvent`, and their argument types.
- `Prism.Ioc` → Provides `ContainerLocator` (for resolving `IEventAggregator`).
- `Prism.Events` → Provides `IEventAggregator`.
- **Depended upon**:
- `IExtraProperty` interface (consumed via constructor and paste logic).
- `PageModifiedEvent`, `TextPastedEvent`, `PageErrorEvent` (published by `PasteCommandClass`).
- `ContainerLocator.Container` (runtime dependency for event aggregation).
---
### **Gotchas**
- **`Paste` method is empty**: The private `Paste(object sender, ExecutedRoutedEventArgs e)` method is declared but has no implementation; all logic resides in `PasteCommandClass.Execute`. This is likely intentional (to satisfy `CommandBinding` requirements), but may confuse readers.
- **`CanExecuteChanged` is never raised**: `PasteCommandClass` implements `ICommand` but does not notify subscribers of `CanExecute` state changes, potentially causing stale UI (e.g., disabled paste buttons).
- **Ambiguous paste behavior**:
- Single-line, non-delimited text triggers `PageModifiedEvent` but *not* `TextPastedEvent`, while multi-line or delimited text triggers `TextPastedEvent` but *not* `PageModifiedEvent`. This asymmetry may lead to inconsistent state tracking.
- The "wipe out built-in effect" assignment (`extraProperty.Key = extraProperty.Key`) is a no-op and serves no functional purpose beyond triggering change notifications if the property setter is implemented non-trivially elsewhere.
- **Clipboard access without safety checks**: `Clipboard.GetText()` may throw exceptions (e.g., in sandboxed environments), but only generic `Exception` handling is present.
- **No null-safety for `tb.Tag`**: The `tb.Tag` value is passed directly in `TextPastedArgs` without null checks.
- **Tight coupling to Prism**: Reliance on `ContainerLocator.Container` for `IEventAggregator` makes unit testing difficult without Prism infrastructure.
- **No cleanup in constructors**: While the finalizer attempts to null `_key`/`_value`, this is unreliable and unnecessary in modern .NET (where `IDisposable` would be preferred if unmanaged resources were involved — none are evident here).