Files
DP44/enriched-qwen3-coder-next/DataPRO/Modules/Channels/ChannelCodes/ViewModel.md

233 lines
11 KiB
Markdown
Raw Normal View History

2026-04-17 14:55:32 -04:00
---
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 Prisms 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 its 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.