182 lines
9.4 KiB
Markdown
182 lines
9.4 KiB
Markdown
|
|
---
|
|||
|
|
source_files:
|
|||
|
|
- Common/DTS.CommonCore/Base/ViewModel/ViewModelBase.cs
|
|||
|
|
- Common/DTS.CommonCore/Base/ViewModel/BaseViewModel.cs
|
|||
|
|
generated_at: "2026-04-16T02:51:04.199849+00:00"
|
|||
|
|
model: "Qwen/Qwen3-Coder-Next-FP8"
|
|||
|
|
schema_version: 1
|
|||
|
|
sha256: "e292dc2f8a859c0e"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# ViewModel
|
|||
|
|
|
|||
|
|
## Documentation: ViewModel Base Classes (`DTS.Common.Base`)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 1. Purpose
|
|||
|
|
|
|||
|
|
This module provides two foundational abstract base classes for implementing ViewModels in the Prism-based WPF UI layer: `ViewModelBase<T>` (a minimal, model-centric base) and `BaseViewModel<TModel>` (a richer, infrastructure-aware base integrating Prism, Unity DI, and region management). `ViewModelBase<T>` serves as a low-level abstraction for custom ViewModel implementations requiring explicit control over model lifecycle, async initialization, and property change notifications. `BaseViewModel<TModel>` extends this with Prism-specific services (event aggregator, region manager, container), built-in command infrastructure (`CloseCommand`, `ConfirmationRequest`), and standardized initialization/cleanup hooks—aiming to reduce boilerplate in typical ViewModels while enforcing consistent lifecycle management and status reporting.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. Public Interface
|
|||
|
|
|
|||
|
|
#### `ViewModelBase<T>` (in `ViewModelBase.cs`)
|
|||
|
|
|
|||
|
|
- **`public object Model { get; set; }`**
|
|||
|
|
Holds the underlying model object. Stored as `object`; cast to `T` internally.
|
|||
|
|
|
|||
|
|
- **`public bool IsBusy { get; protected set; }`**
|
|||
|
|
Read-only public property indicating whether an asynchronous operation is in progress. Protected setter allows derived classes to update it.
|
|||
|
|
|
|||
|
|
- **`public virtual bool IsDirty { get; protected set; }`**
|
|||
|
|
Read-only public property indicating whether the `Model` has unsaved changes. Virtual to allow overriding.
|
|||
|
|
|
|||
|
|
- **`public virtual event EventHandler<ErrorEventArgs> ErrorOccurred;`**
|
|||
|
|
Event raised when an error occurs during processing.
|
|||
|
|
|
|||
|
|
- **`public virtual event PropertyChangedEventHandler PropertyChanged;`**
|
|||
|
|
Standard `INotifyPropertyChanged` event for property change notifications.
|
|||
|
|
|
|||
|
|
- **`protected abstract Task<T> InitializeAsync();`**
|
|||
|
|
Override to implement async model initialization. The returned `Task<T>` result is used to populate the `Model` property.
|
|||
|
|
|
|||
|
|
- **`protected abstract void DoRefresh(Func<T> factoryMethod);`**
|
|||
|
|
Override to implement model refresh logic using a static factory method.
|
|||
|
|
|
|||
|
|
- **`protected abstract void OnError(Exception error);`**
|
|||
|
|
Override to raise the `ErrorOccurred` event.
|
|||
|
|
|
|||
|
|
- **`protected abstract void OnModelChanged(T oldValue, T newValue);`**
|
|||
|
|
Override to handle model replacement (e.g., unhooking/hooking event handlers on the old/new model).
|
|||
|
|
|
|||
|
|
- **`protected abstract void OnPropertyChanged(string propertyName);`**
|
|||
|
|
Override to raise the `PropertyChanged` event.
|
|||
|
|
|
|||
|
|
> **Note**: `ViewModelBase<T>` implements `INotifyPropertyChanged` and `IViewModel` (interface not shown), and inherits from `DependencyObject`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### `BaseViewModel<TModel>` (in `BaseViewModel.cs`)
|
|||
|
|
|
|||
|
|
- **`protected IEventAggregator Aggregator { get; }`**
|
|||
|
|
Prism event aggregator for pub/sub messaging.
|
|||
|
|
|
|||
|
|
- **`protected IUnityContainer Container { get; }`**
|
|||
|
|
Unity DI container for service resolution.
|
|||
|
|
|
|||
|
|
- **`protected IRegionManager _regionManager { get; }`**
|
|||
|
|
Prism region manager for view navigation/region population.
|
|||
|
|
|
|||
|
|
- **`public TModel Model { get; set; }`**
|
|||
|
|
Strongly-typed model object (contrasted with `ViewModelBase<T>.Model` which is `object`).
|
|||
|
|
|
|||
|
|
- **`public InteractionRequest<Confirmation> ConfirmationRequest { get; }`**
|
|||
|
|
Prism `InteractionRequest` for displaying confirmation dialogs.
|
|||
|
|
|
|||
|
|
- **`public DelegateCommand<object> CloseCommand { get; }`**
|
|||
|
|
Command to close the view/viewmodel; executes `CloseMethod`, which in turn calls `CleanupAtClose()` → `Cleanup()`.
|
|||
|
|
|
|||
|
|
- **`protected virtual void CloseMethod(object parameter)`**
|
|||
|
|
Default implementation invokes `CleanupAtClose()`.
|
|||
|
|
|
|||
|
|
- **`public virtual void Activated()`**
|
|||
|
|
Hook called after viewmodel activation (e.g., by Prism region navigation). Default is no-op.
|
|||
|
|
|
|||
|
|
- **`public virtual void Initialize()`**
|
|||
|
|
Publishes a `ShowStatus` event with `StatusState.Busy` and `"Loading"` message via `Aggregator`.
|
|||
|
|
|
|||
|
|
- **`public virtual void Initialize(object parameter)`**
|
|||
|
|
Same as above; accepts initialization parameter (unused in current implementation).
|
|||
|
|
|
|||
|
|
- **`public virtual void Initialize(object parameter, object model)`**
|
|||
|
|
Placeholder with no implementation.
|
|||
|
|
|
|||
|
|
- **`public virtual async Task InitializeAsync()`**
|
|||
|
|
Async version of `Initialize()`, marshals `ShowStatus` publish to UI thread via `Dispatcher.CurrentDispatcher.InvokeAsync`.
|
|||
|
|
|
|||
|
|
- **`public virtual async Task InitializeAsync(object parameter)`**
|
|||
|
|
Async version of `Initialize(object)`.
|
|||
|
|
|
|||
|
|
- **`public new event PropertyChangedEventHandler PropertyChanged;`**
|
|||
|
|
Shadows base `PropertyChanged` event (likely from `BasePropertyChanged`, not shown).
|
|||
|
|
|
|||
|
|
- **`public bool IsMenuIncluded { get; set; }`**
|
|||
|
|
Flag indicating whether the view includes a menu.
|
|||
|
|
|
|||
|
|
- **`public bool IsNavigationIncluded { get; set; }`**
|
|||
|
|
Flag indicating whether the view includes navigation controls.
|
|||
|
|
|
|||
|
|
- **`public int Percentage { get; set; }`**
|
|||
|
|
Status progress percentage (e.g., for loading bars).
|
|||
|
|
|
|||
|
|
- **`public string IsBusyMessage { get; set; }`**
|
|||
|
|
Custom busy message to display.
|
|||
|
|
|
|||
|
|
- **`public bool IsBusy { get; set; }`**
|
|||
|
|
Busy state flag (redundant with `ViewModelBase<T>.IsBusy`; no synchronization between the two).
|
|||
|
|
|
|||
|
|
- **`public bool IsDirty { get; set; }`**
|
|||
|
|
Dirty state flag (redundant with `ViewModelBase<T>.IsDirty`; no synchronization).
|
|||
|
|
|
|||
|
|
- **`public virtual void Cleanup()`**
|
|||
|
|
Sets `Model` to `default(TModel)`.
|
|||
|
|
|
|||
|
|
- **`public Task CleanupAsync()`**
|
|||
|
|
**Throws `NotImplementedException`** — not implemented.
|
|||
|
|
|
|||
|
|
> **Note**: `BaseViewModel<TModel>` inherits from `BasePropertyChanged` (not shown), implements `IBaseViewModel` (not shown), and requires `TModel : class`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. Invariants
|
|||
|
|
|
|||
|
|
- **`ViewModelBase<T>`**:
|
|||
|
|
- `InitializeAsync()` must return a `Task<T>` whose result is assigned to `Model`.
|
|||
|
|
- `OnModelChanged` must be called whenever `Model` changes (implementation responsibility of derived class).
|
|||
|
|
- `OnPropertyChanged` must be called for all property changes requiring UI updates.
|
|||
|
|
- `OnError` must be called to raise `ErrorOccurred`.
|
|||
|
|
|
|||
|
|
- **`BaseViewModel<TModel>`**:
|
|||
|
|
- `Initialize()`/`InitializeAsync()` *must* publish a `ShowStatus` event with `StatusInfo.StatusState.Busy` and `"Loading"` message during initialization.
|
|||
|
|
- `CloseCommand` always triggers `Cleanup()` (via `CleanupAtClose()`).
|
|||
|
|
- `Cleanup()` sets `Model = default(TModel)`.
|
|||
|
|
- `IsBusy`, `IsDirty`, `Percentage`, `IsBusyMessage`, `IsMenuIncluded`, and `IsNavigationIncluded` are *not* synchronized with any internal state machine — their values are purely application-defined.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. Dependencies
|
|||
|
|
|
|||
|
|
#### `ViewModelBase<T>`
|
|||
|
|
- **System namespaces**: `System`, `System.ComponentModel`, `System.Diagnostics`, `System.IO`, `System.Threading.Tasks`, `System.Windows`.
|
|||
|
|
- **Implements**: `INotifyPropertyChanged`, `IViewModel` (external interface).
|
|||
|
|
- **Inherits**: `DependencyObject`.
|
|||
|
|
- **Used by**: Any ViewModel requiring low-level control over model lifecycle and notifications.
|
|||
|
|
|
|||
|
|
#### `BaseViewModel<TModel>`
|
|||
|
|
- **Prism namespaces**: `Microsoft.Practices.Prism.Events`, `Microsoft.Practices.Prism.Commands`, `Microsoft.Practices.Prism.Regions`, `Microsoft.Practices.Prism.Interactivity.InteractionRequest`.
|
|||
|
|
- **Unity DI**: `Microsoft.Practices.Unity`.
|
|||
|
|
- **Internal namespaces**: `DTS.Common.Events` (for `ShowStatus`, `StatusInfo`, `Strings`).
|
|||
|
|
- **Inherits**: `BasePropertyChanged` (unseen, assumed to provide `INotifyPropertyChanged`).
|
|||
|
|
- **Implements**: `IBaseViewModel` (external interface).
|
|||
|
|
- **Depends on**:
|
|||
|
|
- `IRegionManager`, `IEventAggregator`, `IUnityContainer` (injected via constructor).
|
|||
|
|
- `Strings.Strings.Loading` (string resource).
|
|||
|
|
- `Dispatcher.CurrentDispatcher` (for async marshaling).
|
|||
|
|
- **Used by**: Most ViewModels in the application requiring Prism integration.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5. Gotchas
|
|||
|
|
|
|||
|
|
- **`BaseViewModel<TModel>.CleanupAsync()` is unimplemented** — throws `NotImplementedException`. Do not call this method.
|
|||
|
|
- **Redundant state flags**: Both `ViewModelBase<T>` and `BaseViewModel<TModel>` define `IsBusy` and `IsDirty`. There is *no synchronization* between them. If a class inherits from both (or uses both), these flags may diverge.
|
|||
|
|
- **`Model` type mismatch**: `ViewModelBase<T>.Model` is `object`, while `BaseViewModel<TModel>.Model` is strongly-typed `TModel`. Mixing these bases may lead to casting issues.
|
|||
|
|
- **`Initialize(object parameter, object model)` is a no-op** — despite accepting parameters, it does nothing. Initialization logic must be implemented in other overloads.
|
|||
|
|
- **`BaseViewModel` uses `new event PropertyChangedEventHandler`** — this shadows any base `PropertyChanged` (e.g., from `BasePropertyChanged`), which may cause confusion if event handlers are attached to the wrong reference.
|
|||
|
|
- **No automatic `IsBusy`/`IsDirty` management**: Derived classes must manually update `IsBusy`/`IsDirty` (in `ViewModelBase<T>`) or rely on application logic (in `BaseViewModel<TModel>`).
|
|||
|
|
- **`DoRefresh`’s contract is underspecified**: The source does not clarify how/when `DoRefresh` should be called or how it interacts with `InitializeAsync`.
|
|||
|
|
- **`Strings.Strings.Loading` is referenced but not defined here** — assumes a `Strings` resource class exists in `DTS.Common` with a `Loading` property.
|
|||
|
|
|
|||
|
|
> **None identified from source alone** for `ViewModelBase<T>` beyond its minimal design. For `BaseViewModel<TModel>`, the above are the primary concerns.
|