233 lines
11 KiB
Markdown
233 lines
11 KiB
Markdown
|
|
---
|
|||
|
|
source_files:
|
|||
|
|
- DataPRO/Modules/Channels/ChannelCodes/ViewModel/ChannelCodesListViewModel.cs
|
|||
|
|
generated_at: "2026-04-16T04:56:00.828392+00:00"
|
|||
|
|
model: "Qwen/Qwen3-Coder-Next-FP8"
|
|||
|
|
schema_version: 1
|
|||
|
|
sha256: "a1a75e2ca7b7b574"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# ViewModel
|
|||
|
|
|
|||
|
|
### **1. Purpose**
|
|||
|
|
|
|||
|
|
The `ChannelCodesListViewModel` class serves as the view model for managing and displaying channel codes (both ISO and User-defined) in a data entry UI. It orchestrates data loading, filtering, sorting, editing (including paste, copy, delete, and validation), and persistence operations for channel codes. It acts as the intermediary between the UI (`IChannelCodesListView`) and the underlying data layer (`ChannelCode`, `ChannelCodeType`), leveraging Prism’s event aggregation for decoupled communication and Unity for dependency injection. Its primary role is to expose collections of channel codes (`ISOChannelCodes`, `UserChannelCodes`) and provide methods to manipulate and validate them before saving to the database.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **2. Public Interface**
|
|||
|
|
|
|||
|
|
#### **Properties**
|
|||
|
|
- `IChannelCodesListView View { get; set; }`
|
|||
|
|
Reference to the associated view; view model sets `View.DataContext = this`.
|
|||
|
|
|
|||
|
|
- `InteractionRequest<Notification> NotificationRequest { get; }`
|
|||
|
|
Prism interaction request used to show notification popups (e.g., errors, info).
|
|||
|
|
|
|||
|
|
- `InteractionRequest<Confirmation> ConfirmationRequest { get; }`
|
|||
|
|
Prism interaction request used to prompt for user confirmation (e.g., delete).
|
|||
|
|
|
|||
|
|
- `event PropertyChangedEventHandler PropertyChanged`
|
|||
|
|
Standard `INotifyPropertyChanged` implementation; `OnPropertyChanged` raises it.
|
|||
|
|
|
|||
|
|
- `List<IChannelCode> AllISOChannelCodes { get; set; }`
|
|||
|
|
Full list of ISO channel codes (including blank trailing row), used for sorting/filtering.
|
|||
|
|
|
|||
|
|
- `List<IChannelCode> AllUserChannelCodes { get; set; }`
|
|||
|
|
Full list of User channel codes (including blank trailing row), used for sorting/filtering.
|
|||
|
|
|
|||
|
|
- `ObservableCollection<IChannelCode> ISOChannelCodes { get; set; }`
|
|||
|
|
Filtered/sorted view of `AllISOChannelCodes`, bound to the UI.
|
|||
|
|
|
|||
|
|
- `ObservableCollection<IChannelCode> UserChannelCodes { get; set; }`
|
|||
|
|
Filtered/sorted view of `AllUserChannelCodes`, bound to the UI.
|
|||
|
|
|
|||
|
|
- `ViewModes ViewMode { get; set; }`
|
|||
|
|
Current view mode (`ISO` or `User`). Changing it updates `ISOVisibility`/`UserVisibility`.
|
|||
|
|
|
|||
|
|
- `Visibility ISOVisibility { get; }`
|
|||
|
|
`Visible` if `ViewMode == ViewModes.ISO`, else `Collapsed`.
|
|||
|
|
|
|||
|
|
- `Visibility UserVisibility { get; }`
|
|||
|
|
`Visible` if `ViewMode == ViewModes.User`, else `Collapsed`.
|
|||
|
|
|
|||
|
|
- `bool IsBusy { get; set; }`
|
|||
|
|
Bound to busy indicator; updated via `OnBusyIndicatorNotification`.
|
|||
|
|
|
|||
|
|
- `bool IsDirty { get; private set; }` *(Note: never set in source — likely unused or bug)*
|
|||
|
|
|
|||
|
|
- `bool IsReadOnly { get; set; }`
|
|||
|
|
Controls editability of the view (e.g., based on permissions).
|
|||
|
|
|
|||
|
|
- `bool IsMenuIncluded { get; set; }`, `bool IsNavigationIncluded { get; set; }`
|
|||
|
|
UI layout toggles.
|
|||
|
|
|
|||
|
|
- `object Page { get; private set; }`
|
|||
|
|
Identifier for the page this VM serves (used in events to route to correct consumer).
|
|||
|
|
|
|||
|
|
- `Func<IList<IChannelCode>> ChannelCodesFunc { get; }`
|
|||
|
|
Returns `GetAllChannels()` → `ChannelCode.ChannelCodes`.
|
|||
|
|
|
|||
|
|
- `bool ShowISOStringBuilder { get; set; }`, `bool ShowChannelCodeLookupHelper { get; set; }`, `bool UniqueISOCodesRequired { get; set; }`
|
|||
|
|
UI configuration flags.
|
|||
|
|
|
|||
|
|
#### **Methods**
|
|||
|
|
- `void SetPage(object page)`
|
|||
|
|
Sets the `Page` identifier used in events.
|
|||
|
|
|
|||
|
|
- `void OnSetActive()`
|
|||
|
|
Loads existing channel codes from DB (`ChannelCode.GetExistingChannelCodes`), populates `AllISOChannelCodes`/`AllUserChannelCodes`, adds blank trailing rows, sorts, filters, and updates `ISOChannelCodes`/`UserChannelCodes`. Logs exceptions.
|
|||
|
|
|
|||
|
|
- `void Unset()`
|
|||
|
|
Clears all channel code collections and raises property change notifications.
|
|||
|
|
|
|||
|
|
- `void Initialize()`, `void Initialize(object)`, `Task InitializeAsync()`, `void Activated()`, `void Cleanup()`, `Task CleanupAsync()`
|
|||
|
|
No-op stubs (likely required by Prism interface contracts).
|
|||
|
|
|
|||
|
|
- `bool Save()`
|
|||
|
|
Persists changes in three steps:
|
|||
|
|
1. Deletes removed codes (those with valid IDs not in `All*ChannelCodes`).
|
|||
|
|
2. Updates existing codes (those with valid IDs).
|
|||
|
|
3. Inserts new codes (those without valid IDs and not blank).
|
|||
|
|
Returns `true` on success; publishes `PageModifiedEvent(Status.Saved)` or `PageErrorEvent` on failure.
|
|||
|
|
|
|||
|
|
- `void Remove(IChannelCode channel)`
|
|||
|
|
Removes a channel from both `All*` and `*ChannelCodes` lists, adds a blank row if the last row was removed, and publishes `PageModifiedEvent(Status.Modified)`.
|
|||
|
|
|
|||
|
|
- `void MarkModified(IChannelCode channel)`
|
|||
|
|
Ensures a blank row is added after the modified channel (if it’s the last visible row), and publishes `PageModifiedEvent(Status.Modified)`.
|
|||
|
|
|
|||
|
|
- `void Validate(bool bDisplayWindow)`
|
|||
|
|
Validates all ISO/User codes: checks for missing code/name, duplicate code+name pairs. Populates `errors`/`warnings` lists. If `bDisplayWindow`, publishes `PageErrorEvent`. Returns `true` if no errors.
|
|||
|
|
|
|||
|
|
- `void ValidateISO(ref List<string> errors, ref List<string> warnings)`
|
|||
|
|
Validates ISO codes only (calls `CheckChannelCode`).
|
|||
|
|
|
|||
|
|
- `void ValidateUser(ref List<string> errors, ref List<string> warnings)`
|
|||
|
|
Validates User codes only (calls `CheckChannelCode`).
|
|||
|
|
|
|||
|
|
- `void CopySelected()`
|
|||
|
|
Copies selected items (from current `ViewMode`) into `All*ChannelCodes` (before the trailing blank row), then sorts/filters.
|
|||
|
|
|
|||
|
|
- `void DeleteSelected()`
|
|||
|
|
Deletes selected items (from current `ViewMode`), adds blank row if last item deleted, clears selection, and publishes `PageModifiedEvent(Status.Modified)`.
|
|||
|
|
|
|||
|
|
- `void SetISOSelection(IChannelCode[] channelCodes)`, `void SetUserSelection(IChannelCode[] channelCodes)`
|
|||
|
|
Stores selected items; triggers `PageSelectionChanged` event if `ViewMode` matches.
|
|||
|
|
|
|||
|
|
- `IChannelCode[] SelectedCodes { get; }`
|
|||
|
|
Returns selected items for current `ViewMode`.
|
|||
|
|
|
|||
|
|
- `void Filter(object columnTag, string searchTerm)`
|
|||
|
|
Updates `_searchTermForField` and re-applies filter (`FilterIso`/`FilterUser`).
|
|||
|
|
|
|||
|
|
- `void Sort(object columnTag, bool bColumnClick)`
|
|||
|
|
Updates sort field/direction and re-applies sort (`SortIso`/`SortUser`) + filter.
|
|||
|
|
|
|||
|
|
- `void OnRaiseNotification(NotificationContentEventArgs)`
|
|||
|
|
Handles `RaiseNotification` events by raising `NotificationRequest`.
|
|||
|
|
|
|||
|
|
- `void OnBusyIndicatorNotification(bool)`
|
|||
|
|
Updates `IsBusy` property.
|
|||
|
|
|
|||
|
|
- `void ReportErrors(string[] errors)`
|
|||
|
|
Publishes `PageErrorEvent`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **3. Invariants**
|
|||
|
|
|
|||
|
|
- **Trailing Blank Row**:
|
|||
|
|
`AllISOChannelCodes` and `AllUserChannelCodes` always contain a trailing blank `ChannelCode` (added in `OnSetActive`, `Remove`, `MarkModified`, `Paste*`, `CopySelected`, `DeleteSelected`). This enables adding new entries.
|
|||
|
|
|
|||
|
|
- **Filtering Logic**:
|
|||
|
|
`ISOChannelCodes`/`UserChannelCodes` only include rows where:
|
|||
|
|
- Both `Code` and `Name` are blank, **or**
|
|||
|
|
- `Code`/`Name` matches current search term (case-insensitive substring match).
|
|||
|
|
|
|||
|
|
- **Sorting Logic**:
|
|||
|
|
Sorting uses `ChannelComparer`, which:
|
|||
|
|
- Treats blank rows (empty `Code` and `Name`) as *lowest priority* (sorted to bottom).
|
|||
|
|
- Uses `NaturalStringComparer` for case-insensitive, culture-aware comparison.
|
|||
|
|
- Sorts `Code`/`Name` fields separately for ISO/User modes.
|
|||
|
|
|
|||
|
|
- **Validation Rules**:
|
|||
|
|
A channel code is invalid if:
|
|||
|
|
- `Code` is missing (for non-blank rows) → error.
|
|||
|
|
- `Name` is missing (for non-blank rows) → error.
|
|||
|
|
- Duplicate `Code` + `Name` pair exists → error.
|
|||
|
|
|
|||
|
|
- **Save Order**:
|
|||
|
|
`Save()` processes deletions → updates → inserts to avoid ID/name conflicts.
|
|||
|
|
|
|||
|
|
- **Event Subscription Guard**:
|
|||
|
|
`_bAddListeners` ensures only the *second* instantiation of `ChannelCodesListViewModel` subscribes to `TextPastedEvent`/`ChannelCodeCommittedEvent` (a workaround for app startup ordering).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **4. Dependencies**
|
|||
|
|
|
|||
|
|
#### **Imports/Usings (External Dependencies)**
|
|||
|
|
- `Prism.Events` (`IEventAggregator`, `EventBase`)
|
|||
|
|
- `Prism.Regions` (`IRegionManager`)
|
|||
|
|
- `Unity` (`IUnityContainer`)
|
|||
|
|
- `DTS.Common.*`:
|
|||
|
|
- `Enums.Channels.ChannelEnumsAndConstants`
|
|||
|
|
- `Events.*` (`TextPastedEvent`, `ChannelCodeCommittedEvent`, `PageModifiedEvent`, `PageErrorEvent`, `PageSelectionChanged`)
|
|||
|
|
- `Interface.Channels.ChannelCodes.*` (`IChannelCodesListView`, `IChannelCodesListViewModel`, `IChannelCode`)
|
|||
|
|
- `Utilities.*` (`NaturalStringComparer`, `Logging.APILogger`)
|
|||
|
|
- `Interactivity` (`InteractionRequest<Notification/Confirmation>`)
|
|||
|
|
|
|||
|
|
#### **Internal Dependencies**
|
|||
|
|
- `ChannelCodes.Model` (`ChannelCode`, `ChannelCodeType`)
|
|||
|
|
- `DTS.Common.Enums` (`UIItemStatus`, `PageModifiedArg.Status`)
|
|||
|
|
- `Resources.StringResources` (for localized error messages)
|
|||
|
|
|
|||
|
|
#### **Consumers (Inferred)**
|
|||
|
|
- `ChannelCodesListViewModel` is instantiated via Unity (constructor injection).
|
|||
|
|
- `IChannelCodesListView` binds to its properties/methods.
|
|||
|
|
- Other modules subscribe to its events (`PageModifiedEvent`, `PageErrorEvent`, `PageSelectionChanged`).
|
|||
|
|
- `ChannelCodeCommittedEvent` publisher (e.g., a dialog) calls `OnChannelCodesCommitted`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **5. Gotchas**
|
|||
|
|
|
|||
|
|
- **`_bAddListeners` Hack**:
|
|||
|
|
A static boolean `_bAddListeners` prevents *first* instantiation from subscribing to events (due to app startup ordering). Only the *second* instance subscribes. This is fragile and non-obvious.
|
|||
|
|
|
|||
|
|
- **`IsDirty` Never Set**:
|
|||
|
|
The `IsDirty` property is declared but never updated (no assignment in source). Likely dead code or incomplete implementation.
|
|||
|
|
|
|||
|
|
- **`SelectedCodes` Null Safety**:
|
|||
|
|
`GetSelectedISOCodes`/`GetSelectedUserCodes` return `null` if `_selectedISOItems`/`_selectedUserItems` are `null`. Consumers must handle `null`.
|
|||
|
|
|
|||
|
|
- **Paste Behavior Depends on `tag`**:
|
|||
|
|
`PasteIso`/`PasteUser` use `tag` to determine if pasted data is `Code` or `Name`-only (via `bCode = tag.Equals("ISOCode")`). If `tag` is incorrect, data may be misassigned.
|
|||
|
|
|
|||
|
|
- **Duplicate Code Handling**:
|
|||
|
|
Validation flags *duplicate code+name pairs* as errors, but allows duplicate codes with *different names*. This may be intentional (e.g., ISO codes can map to multiple names), but could be confusing.
|
|||
|
|
|
|||
|
|
- **`OnChannelCodesCommitted` Logic Complexity**:
|
|||
|
|
The method builds multiple dictionaries (`lookup2`, `isoLookup`, `userLookup`) to avoid duplicates during batch commits. This is error-prone and duplicates logic in `Save()`.
|
|||
|
|
|
|||
|
|
- **`ParseText` Delimiter Fallback**:
|
|||
|
|
Tries to split by `,`, then `\t`, then `;`. If none yield 2 tokens, the line is skipped. This may silently fail if data uses unexpected delimiters.
|
|||
|
|
|
|||
|
|
- **`Validate` Ignores Blank Rows**:
|
|||
|
|
Blank rows (empty `Code`/`Name`) are skipped during validation. This is correct, but the logic is nested inside `CheckChannelCode`.
|
|||
|
|
|
|||
|
|
- **No Undo/Revert**:
|
|||
|
|
Changes are published as `PageModifiedEvent(Status.Modified)` immediately on edit, but no mechanism to revert is visible.
|
|||
|
|
|
|||
|
|
- **`ChannelCodesFunc` Usage Unclear**:
|
|||
|
|
`ChannelCodesFunc` is exposed but its usage (e.g., by `GetAllChannels`) is not shown in this file. Likely consumed externally.
|
|||
|
|
|
|||
|
|
- **`UniqueISOCodesRequired` Flag**:
|
|||
|
|
The property exists but is never used in validation logic (despite its name). May be a future feature flag.
|
|||
|
|
|
|||
|
|
- **`NotifySelectionChanged` Only Notifies When `ViewMode` Matches**:
|
|||
|
|
Selection change events are only published if the current `ViewMode` matches the selection type (ISO/User). This may cause inconsistent selection state if `ViewMode` changes after selection.
|
|||
|
|
|
|||
|
|
- **No Null Checks on `View`**:
|
|||
|
|
`View.DataContext = this` assumes `View` is non-null, but no validation exists.
|