90 lines
6.5 KiB
Markdown
90 lines
6.5 KiB
Markdown
|
|
---
|
|||
|
|
source_files:
|
|||
|
|
- Common/DTS.Common/BusyIndicatorManager/xBusyIndicator.xaml.cs
|
|||
|
|
- Common/DTS.Common/BusyIndicatorManager/BusyIndicatorManager.cs
|
|||
|
|
generated_at: "2026-04-16T02:51:55.325916+00:00"
|
|||
|
|
model: "Qwen/Qwen3-Coder-Next-FP8"
|
|||
|
|
schema_version: 1
|
|||
|
|
sha256: "abbbba42817259f5"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# BusyIndicatorManager
|
|||
|
|
|
|||
|
|
## Documentation: BusyIndicatorManager Module
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 1. Purpose
|
|||
|
|
This module provides a centralized, singleton-based busy indicator management system for WPF applications using Prism. It coordinates multiple concurrent "busy sessions" (identified by integer IDs) and ensures the UI busy indicator reflects the aggregate state: if *any* session is active, the indicator is shown with the message from the *most recently added or updated* active session. It decouples business logic from UI presentation by exposing observable properties (`IsBusy`, `Message`) that can be bound to a `BusyIndicator` control (e.g., `Xceed.Wpf.Toolkit.BusyIndicator`), while allowing multiple components to request and release busy state independently without interfering with each other.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. Public Interface
|
|||
|
|
|
|||
|
|
#### `BusyIndicatorManager` (Singleton Class)
|
|||
|
|
- **`public static BusyIndicatorManager Instance { get; }`**
|
|||
|
|
Thread-safe singleton accessor. Returns the single shared instance of `BusyIndicatorManager`. Uses double-checked locking via `SyncRoot`.
|
|||
|
|
|
|||
|
|
- **`public bool IsBusy { get; }`**
|
|||
|
|
Read-only property indicating whether *any* busy session is currently active. Raises `PropertyChanged` on change (via `BindableBase`).
|
|||
|
|
*Note:* Set only internally via `RaisePropertyChanged`.
|
|||
|
|
|
|||
|
|
- **`public string Message { get; }`**
|
|||
|
|
Read-only property containing the message to display in the busy indicator. Reflects the message of the *last added/updated* active session (see `ShowBusy`/`CloseBusy` logic). Raises `PropertyChanged` on change.
|
|||
|
|
|
|||
|
|
- **`public void ShowBusy(int id, string busyMessage)`**
|
|||
|
|
Registers or updates a busy session identified by `id`.
|
|||
|
|
- If `id` is new: adds `(id, busyMessage)` to `busyParameters`, sets `IsBusy = true`, and sets `Message = busyMessage`.
|
|||
|
|
- If `id` already exists: updates its message in `busyParameters`, sets `IsBusy = true`, and sets `Message = busyMessage` (i.e., *always* updates to the latest message for this session).
|
|||
|
|
|
|||
|
|
- **`public void CloseBusy(int id)`**
|
|||
|
|
Removes the busy session identified by `id`.
|
|||
|
|
- If `id` exists: removes it from `busyParameters`.
|
|||
|
|
- After removal:
|
|||
|
|
- If `busyParameters.Count == 0`: sets `IsBusy = false`, `Message = string.Empty`.
|
|||
|
|
- Else: sets `IsBusy = true`, `Message = busyParameters.Last().Value` (i.e., message from the *last-enumerated* remaining session — *not necessarily the most recent*).
|
|||
|
|
|
|||
|
|
#### `xBusyIndicator` (Partial Class)
|
|||
|
|
- **`public xBusyIndicator()`**
|
|||
|
|
Constructor. Calls `InitializeComponent()` to load XAML resources.
|
|||
|
|
|
|||
|
|
- **`public void Connect(int connectionId, object target)`**
|
|||
|
|
*Currently empty implementation.* No behavior defined in source. Purpose unclear without additional context.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. Invariants
|
|||
|
|
- **Session uniqueness**: Each `id` passed to `ShowBusy`/`CloseBusy` must be unique *per caller* to avoid unintended overwrites or conflicts. The manager does not enforce uniqueness across callers.
|
|||
|
|
- **Message semantics**: `Message` always reflects the value of the *last entry* in the `busyParameters` dictionary (via `Last()`), which depends on dictionary enumeration order (insertion order in .NET 6+ for `Dictionary<TKey, TValue>`). This is *not* guaranteed to be the *most recently added* session if sessions are closed/reopened.
|
|||
|
|
- **State consistency**: `IsBusy` is `true` iff `busyParameters.Count > 0`.
|
|||
|
|
- **Thread safety**: Singleton instantiation is thread-safe via `lock(SyncRoot)`. However, *all other methods (`ShowBusy`, `CloseBusy`)* are **not thread-safe** — concurrent calls may corrupt `busyParameters` (e.g., race in `ContainsKey`/`Add`/`Remove`).
|
|||
|
|
- **No cleanup on ID reuse**: Reusing an `id` after `CloseBusy(id)` will overwrite the previous session’s message (via `ShowBusy`), but no explicit cleanup of stale references occurs beyond the dictionary update.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. Dependencies
|
|||
|
|
|
|||
|
|
#### Dependencies *of* this module:
|
|||
|
|
- **Prism.Mvvm**: Used via `BindableBase` for property change notification (`RaisePropertyChanged`).
|
|||
|
|
- **System.Collections.Generic**: For `Dictionary<int, string>`.
|
|||
|
|
- **System.Linq**: For `Dictionary.Last()` in `CloseBusy`.
|
|||
|
|
- **Xceed.Wpf.Toolkit**: Referenced via `BusyIndicator` usage (implied by `xBusyIndicator.xaml`), though not directly used in the C# code shown.
|
|||
|
|
- **WPF**: `System.Windows.Controls` (likely for `BusyIndicator` control usage in XAML).
|
|||
|
|
|
|||
|
|
#### Dependencies *on* this module:
|
|||
|
|
- Any UI component needing to show/hide a busy indicator must reference `DTS.Common.BusyIndicatorManager` and use `BusyIndicatorManager.Instance.ShowBusy(...)` / `CloseBusy(...)`.
|
|||
|
|
- `xBusyIndicator.xaml` (WPF user control) likely binds to `BusyIndicatorManager.Instance.IsBusy` and `Message` (though binding logic is not in the provided source).
|
|||
|
|
- *Inferred usage pattern*: Components call `ShowBusy(id, msg)` before long-running operations and `CloseBusy(id)` afterward (e.g., in `try/finally` blocks).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5. Gotchas
|
|||
|
|
- **Non-deterministic message on partial close**: In `CloseBusy`, `Message` is set to `busyParameters.Last().Value`. Since `Dictionary<TKey, TValue>` enumeration order is insertion order (in modern .NET), this uses the *oldest remaining session*’s message — *not* the most recently added one. This may cause confusing UI behavior if sessions overlap.
|
|||
|
|
- **No ID validation**: `ShowBusy` and `CloseBusy` accept any `int id`. Negative or zero IDs are allowed but may cause conflicts if callers reuse IDs carelessly.
|
|||
|
|
- **Thread-unsafety**: Methods `ShowBusy` and `CloseBusy` lack synchronization. Concurrent calls (e.g., from background threads) may cause `InvalidOperationException` (e.g., modifying collection during enumeration) or inconsistent state.
|
|||
|
|
- **Empty `Connect` method**: The `xBusyIndicator.Connect` method has no implementation. Its purpose is unclear — possibly a placeholder for future binding logic or event wiring.
|
|||
|
|
- **No timeout/cleanup mechanism**: Idle busy sessions (e.g., due to exceptions skipping `CloseBusy`) persist indefinitely until manually closed.
|
|||
|
|
- **Message overwrites**: Calling `ShowBusy(id, msg)` for an existing `id` overwrites the message but does *not* reset the session’s position in the dictionary (so `Last()` may still refer to an older session).
|
|||
|
|
|
|||
|
|
*None identified from source alone.*
|
|||
|
|
→ *Correction:* Several gotchas *are* apparent (see above).
|