--- 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 NotificationRequest { get; }` Prism interaction request used to show notification popups (e.g., errors, info). - `InteractionRequest ConfirmationRequest { get; }` Prism interaction request used to prompt for user confirmation (e.g., delete). - `event PropertyChangedEventHandler PropertyChanged` Standard `INotifyPropertyChanged` implementation; `OnPropertyChanged` raises it. - `List AllISOChannelCodes { get; set; }` Full list of ISO channel codes (including blank trailing row), used for sorting/filtering. - `List AllUserChannelCodes { get; set; }` Full list of User channel codes (including blank trailing row), used for sorting/filtering. - `ObservableCollection ISOChannelCodes { get; set; }` Filtered/sorted view of `AllISOChannelCodes`, bound to the UI. - `ObservableCollection 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> 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 errors, ref List warnings)` Validates ISO codes only (calls `CheckChannelCode`). - `void ValidateUser(ref List errors, ref List 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`) #### **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.