136 lines
7.8 KiB
Markdown
136 lines
7.8 KiB
Markdown
---
|
||
source_files:
|
||
- DataPRO/Modules/Menu/HamburgerMenu/ViewModel/HamburgerMenuViewModel.cs
|
||
generated_at: "2026-04-16T04:48:30.206063+00:00"
|
||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||
schema_version: 1
|
||
sha256: "462d2b26bf58cd93"
|
||
---
|
||
|
||
# ViewModel
|
||
|
||
## Documentation: `HamburgerMenuViewModel`
|
||
|
||
---
|
||
|
||
### 1. **Purpose**
|
||
|
||
The `HamburgerMenuViewModel` class serves as the MVVM-compliant view model for the application’s hamburger menu UI component. It manages the state and behavior of the menu—including item list, enabled/disabled status, busy state, and context menu construction—while acting as a bridge between the view (`IHamburgerMenuView`) and the broader system via Prism’s event aggregation and Unity dependency injection. It subscribes to system-wide events (e.g., test lifecycle, busy indicators, notifications) to dynamically update its state and exposes interaction requests for modal UI (notifications, confirmations). It is a shared singleton (per container) responsible for rendering a context menu populated from a string array and raising a delegate when menu items are selected.
|
||
|
||
---
|
||
|
||
### 2. **Public Interface**
|
||
|
||
- **`IHamburgerMenuView View { get; set; }`**
|
||
Reference to the associated view; set during construction and used to bind `DataContext`.
|
||
|
||
- **`InteractionRequest<Notification> NotificationRequest { get; }`**
|
||
Prism interaction request used to trigger notification popups (e.g., informational messages).
|
||
|
||
- **`InteractionRequest<Confirmation> ConfirmationRequest { get; }`**
|
||
Prism interaction request used to trigger confirmation dialogs (e.g., yes/no prompts).
|
||
|
||
- **`event PropertyChangedEventHandler PropertyChanged;`**
|
||
Standard `INotifyPropertyChanged` implementation for data binding.
|
||
|
||
- **`void OnPropertyChanged(string propertyName);`**
|
||
Raises the `PropertyChanged` event for the specified property.
|
||
|
||
- **`ContextMenu GetContextMenu();`**
|
||
Returns the lazily-built `ContextMenu` instance. Builds it on first call if not already constructed. Thread-safe via `lock(MyLock)`.
|
||
|
||
- **`void SetMenuItems(string[] items);`**
|
||
Sets the list of menu item labels and immediately rebuilds the context menu.
|
||
|
||
- **`void ClearMenuItems();`**
|
||
Clears the internal `_menuItems` array and triggers a rebuild (resulting in an empty menu).
|
||
|
||
- **`void EnableMenu(bool enable);`**
|
||
Sets `IsEnabled` to `enable`, updating the UI state.
|
||
|
||
- **`void Unset();`**
|
||
Currently empty; no-op.
|
||
|
||
- **`void OnSetActive();`**
|
||
Currently empty; no-op.
|
||
|
||
- **`void Cleanup();`**
|
||
Currently empty; no-op.
|
||
|
||
- **`Task CleanupAsync();`**
|
||
Returns `Task.CompletedTask`; no-op.
|
||
|
||
- **`void Initialize();`**
|
||
Overload variants (`Initialize()`, `Initialize(object)`, `Initialize(object, object)`) are present but all currently no-op.
|
||
|
||
- **`Task InitializeAsync();`**
|
||
Overload variants (`InitializeAsync()`, `InitializeAsync(object)`) are present but all return `Task.CompletedTask`; no-op.
|
||
|
||
- **`void Activated();`**
|
||
Currently empty; no-op.
|
||
|
||
- **`bool IsEnabled { get; private set; }`**
|
||
Indicates whether the menu is enabled. Default `true`. Updated via `EnableMenu()`, `OnTestEvent()`, or internal logic.
|
||
|
||
- **`bool IsBusy { get; set; }`**
|
||
Indicates whether the menu is in a busy state (e.g., during test execution). Bound to `BusyIndicatorChangeNotification` event.
|
||
|
||
- **`bool IsMenuIncluded { get; set; }`**
|
||
Indicates whether the hamburger menu is included in the UI. Not used internally—likely for view-level visibility binding.
|
||
|
||
- **`bool IsNavigationIncluded { get; set; }`**
|
||
Indicates whether navigation controls are included in the UI. Not used internally—likely for view-level visibility binding.
|
||
|
||
- **`MenuItemPressedDelegate MenuItemPressed { get; set; }`**
|
||
Delegate invoked when a menu item is pressed. Signature: `void Delegate(string id)`. The `id` passed is the `Header` of the selected `MenuItem`.
|
||
|
||
---
|
||
|
||
### 3. **Invariants**
|
||
|
||
- `View.DataContext` is always set to `this` in the constructor.
|
||
- `_contextMenu` is lazily initialized on first call to `GetContextMenu()` and rebuilt only when `_menuItems` changes (via `SetMenuItems()` or `ClearMenuItems()`).
|
||
- `_menuItems` is never `null`; it is initialized to `new string[0]` and cleared to `new string[0]`.
|
||
- `IsEnabled` defaults to `true`.
|
||
- `IsBusy` is updated synchronously on the publisher thread (via `ThreadOption.PublisherThread`) in response to `BusyIndicatorChangeNotification`.
|
||
- Thread safety for context menu construction is ensured via `lock(MyLock)` in `GetContextMenu()` and `BuildContextMenu()`.
|
||
- Automation IDs are set on the `ContextMenu` (`"HamburgerMenu"`) and each `MenuItem` (`"MenuItem_{ItemName}"`, with spaces removed).
|
||
- `MenuItemPressed` is invoked with the `Header` string of the selected `MenuItem`.
|
||
|
||
---
|
||
|
||
### 4. **Dependencies**
|
||
|
||
**Imports/References (from source):**
|
||
- `System.ComponentModel`, `System.ComponentModel.Composition` (MEF)
|
||
- `Prism.Events`, `Prism.Regions` (Prism framework)
|
||
- `Unity` (Unity DI container)
|
||
- `DTS.Common.Events` (custom event types: `RaiseNotification`, `BusyIndicatorChangeNotification`, `TestEvent`)
|
||
- `DTS.Common.Interface.Menu.HamburgerMenu` (interface `IHamburgerMenuViewModel`)
|
||
- `DTS.Common.Interactivity` (interaction request types: `Notification`, `Confirmation`)
|
||
- `System.Windows.Controls`, `System.Windows.Automation`
|
||
|
||
**Depends on:**
|
||
- `IHamburgerMenuView` (view interface)
|
||
- `IRegionManager`, `IEventAggregator`, `IUnityContainer` (Prism/Unity infrastructure)
|
||
- `TestEvent`, `BusyIndicatorChangeNotification`, `RaiseNotification` (custom events via `IEventAggregator`)
|
||
|
||
**Used by:**
|
||
- Likely consumed by `IHamburgerMenuView` (WPF view) via data binding.
|
||
- Other modules may publish `TestEvent`, `BusyIndicatorChangeNotification`, or `RaiseNotification` to control its behavior.
|
||
|
||
---
|
||
|
||
### 5. **Gotchas**
|
||
|
||
- **Empty lifecycle methods**: `Initialize*`, `Cleanup*`, `Unset`, `OnSetActive`, and `Activated` are all no-ops. If lifecycle management is expected, this may be incomplete or deferred to the view.
|
||
- **`MenuItemPressed` is a delegate, not an event**: It is declared as `public MenuItemPressedDelegate MenuItemPressed { get; set; }`, meaning it can be overwritten and is not thread-safe for multicast subscriptions. Callers must ensure safe assignment.
|
||
- **`IsDirty` property exists but is never set**: Declared as `public bool IsDirty { get; private set; }`, but never assigned—likely unused or incomplete.
|
||
- **`IsMenuIncluded` and `IsNavigationIncluded` are never set internally**: Only exposed as properties; their values must be set externally (e.g., by view or another VM), but no logic uses them.
|
||
- **`AutomationProperties.AutomationIdProperty` is hardcoded**: Menu item IDs are derived by removing spaces from the header (`item.Replace(" ", "")`). This may cause collisions if headers differ only by whitespace (e.g., `"Run Test"` vs `"RunTest"` both become `"MenuItem_RunTest"`).
|
||
- **`GetContextMenu()` returns the same instance after first build**: Repeated calls return the cached `_contextMenu`. If `_menuItems` changes but `GetContextMenu()` is called before `SetMenuItems()`/`ClearMenuItems()` triggers `BuildContextMenu()`, stale items may be returned.
|
||
- **`OnTestEvent()` toggles `IsEnabled` only on `TestStarted`/`TestEnded`**: If `TestEventArg.Status` is anything else (e.g., `TestRunning`), `IsEnabled` remains unchanged—assumes only two statuses.
|
||
- **Thread safety**: Only `GetContextMenu()` and `BuildContextMenu()` are thread-safe. Other property setters (e.g., `IsBusy`) assume caller handles thread affinity (though `IsBusy` setter uses `OnPropertyChanged`, which may marshal to UI thread depending on binding context—*not guaranteed*).
|
||
- **No unsubscription from events**: The constructor subscribes to events but there is no `IDisposable` implementation or `Unsubscribe()` call—potential memory leak if the VM outlives its intended lifetime.
|
||
|
||
> *None of the above are necessarily bugs, but they are non-obvious and require awareness during maintenance or extension.* |