--- source_files: - DTS Viewer/DTS.Viewer.Modules/DTS.Viewer.GraphList/ViewModel/GraphMainViewModel.cs generated_at: "2026-04-16T13:48:30.268564+00:00" model: "zai-org/GLM-5-FP8" schema_version: 1 sha256: "09077834bf97663d" --- # Documentation: GraphMainViewModel ## 1. Purpose The `GraphMainViewModel` class acts as the presentation logic controller for the Graph List module. It is responsible for transforming raw test summary data into a hierarchical, displayable tree of test channels (`TestChannelsTree`), managing the state of channel selection and locking (e.g., for reporting vs. viewing), and mediating communication between the data layer and the UI via Prism's `IEventAggregator`. It handles the logic for assigning distinct colors to channels, filtering channel lists, and enforcing constraints such as the maximum number of lockable channels. ## 2. Public Interface ### Properties * **`IFilterView FilterView`**: Gets the filter view instance associated with this view model. * **`IGraphMainView View`**: Gets or sets the associated view interface. * **`IBaseViewModel Parent`**: Gets or sets the parent view model (used to scope event handling). * **`InteractionRequest NotificationRequest`**: Gets the interaction request for displaying notifications. * **`InteractionRequest ConfirmationRequest`**: Gets the interaction request for displaying confirmations. * **`ObservableCollection ChannelList`**: Gets or sets the full list of available channels. Setting this updates `IsFilterEnabled` and resets `FilteredChannelList`. * **`ObservableCollection FilteredChannelList`**: Gets or sets the list of channels currently displayed (after filtering). Setting this triggers a rebuild of `TestChannelsTree`. * **`List LockedChannelList`**: Gets or sets the list of channels currently locked by the user. * **`List SelectedChannelList`**: Gets or sets the list of channels currently selected by the user. * **`ObservableCollection TestChannelsTree`**: Gets or sets the hierarchical structure of tests, groups, and channels bound to the UI. * **`bool IsFilterEnabled`**: Gets or sets a value indicating whether the filter UI is enabled (true if `ChannelList` has items). * **`string SelectedGroupName`**: Gets or sets the name of the currently selected group. * **`string LockedGroupName`**: Gets or sets the name of the currently locked group. * **`object ContextGraphMainRegion`**: Gets or sets the data context for the `GraphMainRegion` within the view. * **`bool TestModified`**: Gets or sets a value indicating whether the underlying test has been modified. ### Methods * **`void Initialize()`**: Overrides base `Initialize`. Currently empty. * **`void Initialize(object parameter)`**: Overrides base `Initialize`. Sets the `Parent`, determines if locked-only mode is active (if Parent is `IPSDReportMainViewModel`), initializes the `FilterView`, and subscribes to events. * **`void Activated()`**: Overrides base `Activated`. Publishes a `FilterParameterChangedEvent` with an empty parameter to reset the filter. * **`void PublishSelectedChannels()`**: Publishes the combined list of `LockedChannelList` and (optionally) `SelectedChannelList` via `GraphSelectedChannelsNotification`. * **`void OnFilterChanged(FilterParameterArgs args)`**: Filters `ChannelList` based on the string provided in `args.Param` and updates `FilteredChannelList`. * **`void AddLockedGroupChannels(string testName, string groupName, List channels, bool isLocked)`**: Adds or removes a group of channels to/from the `LockedChannelList`. Updates channel colors and locks. Triggers `UpdateChannelLocks`. * **`void AddLockedChannel(ITestChannel channel, bool isLocked)`**: Adds or removes a single channel to/from the `LockedChannelList`. Manages selection state if the channel was previously selected. Triggers `UpdateChannelLocks`. * **`void AddSelectedGroupChannels(string groupName, List channels)`**: Replaces the `SelectedChannelList` with the provided channels and assigns colors. * **`void AddSelectedGroup(TestGroup group)`**: Marks a specific `TestGroup` as selected. * **`void AddSelectedChannel(ITestChannel channel, bool reset)`**: Adds a channel to `SelectedChannelList`. If `reset` is true, clears existing selection first. Handles graph channels by selecting their group instead. * **`void AddSelectedChannel(ITestChannel channel)`**: Overload that resets the selection before adding the new channel. * **`void TestChannelsTree_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)`**: Handles collection change events for the tree, wiring up `PropertyChanged` listeners. * **`void GraphList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)`**: Handles collection change events for the channel list, wiring up `PropertyChanged` listeners. ## 3. Invariants * **Maximum Locked Channels**: The system enforces a hard limit of **8** locked channels (`MAX_LOCKED_CHANNELS`). The `UpdateChannelLocks` method disables the ability to lock further channels once this limit is reached. * **Color Assignment**: Channels are assigned colors from a predefined list (`_graphColors`). Transparent is reserved (default). The system attempts to assign unused colors first; if all colors are exhausted, it cycles through them using modulo arithmetic. * **Locked vs. Selected**: A channel cannot be both locked and selected. Locking a channel removes it from `SelectedChannelList`. * **Parent Scoping**: Event handlers (e.g., `OnTestSummaryChanged`) verify that the event's `ParentVM` matches the instance's `Parent` before processing, ensuring multiple instances of the view model do not interfere with each other. * **Locked-Only Mode**: If the `Parent` is of type `IPSDReportMainViewModel`, the `_lockedOnly` flag is set to `true`. In this mode, `PublishSelectedChannels` only publishes locked channels, ignoring selected channels. ## 4. Dependencies ### Internal Dependencies * `DTS.Common.Base`: `BaseViewModel` * `DTS.Common.Enums.Sensors`: `CalibrationBehaviors`, `IsoViewMode` * `DTS.Common.Events`: `TestSummaryChangeNotificationArg`, `GraphClearNotificationArg`, `FilterParameterArgs`, etc. * `DTS.Common.Interactivity`: `InteractionRequest`, `Notification`, `Confirmation` * `DTS.Common.Interface`: `ITestChannel`, `ITestSummary`, `IBaseViewModel`, `IFilterView`, etc. * `DTS.Common.Utils`: `Utils` static helper. * `DTS.Serialization.SliceRaw.File.PersistentChannel`: Used for ISO code retrieval delegate. ### External Dependencies * `Prism.Events`: `IEventAggregator` (Pub/Sub events). * `Prism.Regions`: `IRegionManager` (Navigation/Region management). * `Unity`: `IUnityContainer` (Dependency Injection). * `System.Windows.Media`: `Color`, `Colors`. ## 5. Gotchas * **Explicit GC Collection**: The method `CleanSelection()` explicitly calls `GC.Collect()`. This is generally discouraged in production code and may indicate a historical attempt to force cleanup of unmanaged resources or large objects. * **Lazy Loading Side-Effect**: In `OnTestSummaryChanged`, there is a check `if (l.Channels.FirstOrDefault() == null)`. If the first channel is null, it forces a load operation using `Utils.SetChannelInfo`. This implies that `ITestSummary` objects passed to this VM might be partially initialized stubs. * **Commented Out Event Subscription**: The subscription to `GraphChannelsReadCompletedNotification` is commented out with the note "It does not work", suggesting incomplete functionality or a known bug in the event chain. * **Empty Event Handlers**: Several event handlers (`OnCalibrationBehaviorSettableInViewerChanged`, `TestChannelsTree_PropertyChanged`, `GraphList_PropertyChanged`) contain empty bodies or logic that does nothing (e.g., `if (e.PropertyName != "TestChannelsTree") { }`). This suggests either dead code or placeholders for future logic. * **Calibration Behavior Mutation**: When `CalibrationBehaviors.UseBothIfAvailable` is active, the code modifies the `ChannelDescriptionString` of the channel objects directly by appending "(NonLinear)" or "(Linear)". This mutates the data objects which might be shared elsewhere. * **Color Index Logic**: The logic in `GetNextColor` for handling the case where `channelCount >= _graphColors.Count` contains a redundant check (`if (nextColorIndex >= _graphColors.Count)`), as the modulo math ensures the index is within bounds, though the `+1` offset logic makes it slightly fragile regarding the list size.