Files

149 lines
9.7 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
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<IGroup>`.
#### 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 groups `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<string>, List<int>)`**
Constructs a `Group` from a data reader and hardware/test setup lists.
- **`IGroup CreateGroup(IGroupDbRecord, List<string>, List<int>)`**
Constructs a `Group` from a database record and hardware/test setup lists.
- **`IGroup CreateGroup(List<string>)`**
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 users 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<Notification> NotificationRequest`** For displaying notification popups.
- **`InteractionRequest<Confirmation> ConfirmationRequest`** For confirmation dialogs.
- **`event PropertyChangedEventHandler PropertyChanged`** Implements `INotifyPropertyChanged`.
- **`ObservableCollection<IGroup> 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<GroupFields, string>`** 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.