Files

90 lines
6.5 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
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 sessions 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 sessions 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).