--- 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`). 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`. - **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` 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).