--- source_files: - DataPRO/Modules/Groups/GroupList/ViewModel/GroupListViewModel.cs generated_at: "2026-04-16T04:47:12.822358+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "efb48e4bf6e2c1c7" --- # ViewModel ## Documentation: `GroupListViewModel` --- ### 1. Purpose `GroupListViewModel` is the core view model for the Group List UI module. It manages the display, filtering, sorting, selection, and lifecycle of `IGroup` entities within the application. It acts as the intermediary between the `IGroupListView` view and the underlying group data model (`Group`), handling user interactions (e.g., double-click to edit, filtering/sorting), coordinating background operations (e.g., loading test setup lists), and publishing/subscribing to domain events via `IEventAggregator`. It supports both single- and multi-select group operations and integrates with the Prism region management and Unity DI container. --- ### 2. Public Interface #### Constructors - **`GroupListViewModel(IGroupListView view, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer)`** Initializes the view model. Sets up the view binding, event subscriptions (e.g., `RaiseNotification`, `BusyIndicatorChangeNotification`), and initializes `SelectedGroupItems` as an `ObservableCollection`. #### Public Methods - **`void ClearAllFilters()`** Clears all per-field filter terms stored in `_filterByField`. - **`void MouseDoubleClick(int index)`** If a single group is selected and `index` is valid, publishes a `GroupListEditGroupEvent` with the selected group’s `Id`. - **`void Filter(string term)`** Sets `CurrentSearchTerm` to `term`, then re-applies sorting (which triggers filtering and re-sorting of `Groups`). - **`IGroup GetGroup(int? id, bool updateTags = true)`** Returns the group with the given `id` (if `id >= 0`). If `updateTags` is `true`, updates the global tag cache before fetching. Returns a new empty `Group()` if not found. - **`IGroup GetGroup(string displayName)`** Returns the *non-embedded* group matching `displayName`, preferring non-embedded over embedded groups (for backward compatibility with pre-2.0 group types). Returns `null` if none found. - **`IGroup[] GetGroups(int[] ids)`** Returns an array of groups whose `Id`s are in `ids`, preserving order only in that groups *not* in `ids` are removed (i.e., order is not guaranteed to match `ids` order). - **`IGroup[] GetAllGroups()`** Returns *all* groups (via `Group.GetAllGroups()`), unfiltered and unsorted. - **`void DeleteGroups(int[] ids)`** Deletes groups with given `ids`. For each group, nullifies `StaticGroupId` on all related embedded groups, deletes the group, updates `AllGroups`, and re-applies filters. Publishes errors via `PageErrorEvent`. - **`IGroup CreateGroup()`** Returns a new unsaved `Group(true)` (likely a new transient instance). - **`IGroup CreateGroup(SqlDataReader, List, List)`** Constructs a `Group` from a data reader and hardware/test setup lists. - **`IGroup CreateGroup(IGroupDbRecord, List, List)`** Constructs a `Group` from a database record and hardware/test setup lists. - **`IGroup CreateGroup(List)`** Constructs a new `Group` with the given `includedHardwareStringList`. - **`void Filter(object tag, string term)`** Parses `tag` as a `GroupFields` enum value; if successful, stores `term` in `_filterByField[tag]`, sets `_sortField`, and calls `Filter(term)`. - **`void OnSetActive(object page, bool groupTile, object o)`** Sets `Page`, filters `AllGroups` to groups compatible with the current user’s role/tags, and either calls `GetTestSetupListsAsync()` (if `groupTile == true`) or `Sort()`. - **`private void GetTestSetupListsAsync()`** Runs asynchronously: sets app status to busy, iterates over `AllGroups` in parallel to call `g.SetTestSetupLists()`, then re-sorts on the UI thread. - **`void Unset()`** Clears `AllGroups`, `Groups`, filters, and publishes `ListViewStatusEvent` with status `Unloaded`. - **`void Sort(object o, bool bColumnClick)`** Sorts `AllGroups` by `GroupFields` (parsed from `o`), applies `CurrentSearchTerm` and per-field filters (`GroupFilter`), and sets `Groups`. Updates sort direction on column click. - **`void Cleanup()` / `Task CleanupAsync()` / `void Initialize()` / `Task InitializeAsync()` / `void Activated()`** No-op stubs; likely required by Prism or custom interfaces but not implemented. #### Public Properties - **`IGroupListView View`** – Bound view instance. - **`InteractionRequest NotificationRequest`** – For displaying notification popups. - **`InteractionRequest ConfirmationRequest`** – For confirmation dialogs. - **`event PropertyChangedEventHandler PropertyChanged`** – Implements `INotifyPropertyChanged`. - **`ObservableCollection SelectedGroupItems`** – Tracks selected groups; raises `GroupListGroupSelectedEvent` on change. - **`IGroup[] Groups`** – Currently displayed (filtered + sorted) groups. - **`bool IsBusy`** – Bound to busy indicator; updated via `OnBusyIndicatorNotification`. - **`bool IsMenuIncluded` / `bool IsNavigationIncluded`** – UI toggle flags. - **`string ListViewId`** – `"GroupListView"`; used for event scoping. - **`int SelectedGroupIndex`** – Index of selected group in `Groups`; default `-1`. #### Private Properties (used in logic) - **`_filterByField: Dictionary`** – Per-field filter terms. - **`CurrentSearchTerm: string`** – Global search term. - **`_sortField: GroupFields`** – Current sort field (default `LastModified`). - **`_sortAscending: bool`** – Sort direction (default `false` = descending). - **`_comparer: GroupComparer`** – Comparison logic for sorting. --- ### 3. Invariants - `Groups` is always a filtered and sorted subset of `AllGroups`. - `AllGroups` is always filtered by user role and tag compatibility (via `currentUser.TagCompatible(@group.TagIDs)`). - `SelectedGroupItems` is an `ObservableCollection`; changes trigger `GroupListGroupSelectedEvent`. - `_filterByField` stores per-field filter terms; `CurrentSearchTerm` is a global term applied in `Sort`. - `Groups` is sorted by `_sortField` in `_sortAscending` order using `GroupComparer`. - `IsBusy` is updated via `BusyIndicatorChangeNotification` event (thread-safe subscription on `PublisherThread`). - `SelectedGroupItems.CollectionChanged` handler skips updates during flagged "updating" states (via `DTS.Common.Enums.SelectedItemsStatus.GetUpdating`). --- ### 4. Dependencies #### Imports / Dependencies Used - **Prism**: `IEventAggregator`, `IRegionManager`, `Unity`, `Prism.Events`, `Prism.Regions`, `Prism.Interactivity`. - **DTS Common Libraries**: - `DTS.Common.Events.*` (e.g., `RaiseNotification`, `BusyIndicatorChangeNotification`, `PageErrorEvent`, `AppStatusEvent`, `ProgressBarEvent`, `ListViewStatusEvent`, `GroupListEditGroupEvent`, `GroupListGroupSelectedEvent`) - `DTS.Common.Interface.Groups.*` (`IGroup`, `IGroupListView`, `IGroupListViewModel`) - `DTS.Common.Enums.Groups.GroupList.*` (`GroupFields`, `ListViewStatusArg`) - `DTS.Common.Storage.*` (`DbOperations`) - `DTS.Slice.Users.User` - `DTS.Common.Classes.Tags.TagsInstance` - **.NET**: `System.ComponentModel`, `System.Collections.ObjectModel`, `System.Threading.Tasks`, `System.Linq`, `System.Collections.Specialized`, `System.Windows`, `System.Data.SqlClient.SqlDataReader`. #### Dependencies on This Module - `GroupListViewModel` is exported via MEF (`[PartCreationPolicy(CreationPolicy.Shared)]`) and likely consumed by DI container resolution for `IGroupListViewModel`. - `IGroupListView` must be provided at construction (likely via Prism view injection). - Other modules subscribe to events it publishes: `GroupListEditGroupEvent`, `GroupListGroupSelectedEvent`, `PageErrorEvent`, `ListViewStatusEvent`. --- ### 5. Gotchas - **Sorting logic comment**: The `Sort` method contains commented-out logic suggesting a potential bug or inconsistency in handling already-sorted lists (especially for `LastModified`). This may cause unexpected sort direction toggling. - **`GetGroup(string displayName)` behavior**: Returns only non-embedded groups *if* any exist; otherwise returns `null`. This may hide embedded groups unexpectedly. - **`GetGroups(int[] ids)` order**: Output order is not guaranteed to match input `ids` order (uses `Contains` in reverse iteration). - **`SelectedGroupItems` setter**: Unsubscribes from `CollectionChanged` only if `_selectedGroupItems` was non-null *before* assignment. If `SelectedGroupItems` is set multiple times, old handlers may leak if not handled carefully. - **`OnSetActive` filtering**: Filtering by user role/tag compatibility happens *only* in `OnSetActive`, not in `Filter` or `Sort`. Thus, filtering/sorting operates on already-filtered `AllGroups`. - **`GetTestSetupListsAsync`**: Runs `SetTestSetupLists()` in parallel (`AsParallel().ForAll`), but UI thread re-sort is done via `Dispatcher.Invoke`. Potential for race conditions if `SetTestSetupLists()` modifies group state accessed during sort. - **`GroupFilter` case sensitivity**: Uses `CompareOptions.OrdinalIgnoreCase` for all fields, including `LastModified` (stringified) — may yield unexpected matches (e.g., "12" matches "12" but also "12" in "12/12/2023"). - **No validation on `DeleteGroups`**: Does not check if groups are in use (e.g., by tests); relies on underlying `Group.Delete(id)` to handle errors (which are then published via `PageErrorEvent`). - **`IsDirty` property**: Declared but never set; always `false`. - **`Cleanup()`/`Initialize()` stubs**: All `Initialize*`, `Cleanup*`, and `Activated()` methods are empty — may indicate incomplete implementation or reliance on external lifecycle management. None identified beyond those above.