6.7 KiB
source_files, generated_at, model, schema_version, sha256
| source_files | generated_at | model | schema_version | sha256 | ||
|---|---|---|---|---|---|---|
|
2026-04-16T04:29:09.171697+00:00 | Qwen/Qwen3-Coder-Next-FP8 | 1 | 85d98116aaeec020 |
App
Documentation: WaitCursor and App Module (DatabaseImporter)
1. Purpose
This module provides UI-level cursor and application state management during long-running operations in a WPF-based database import workflow. Specifically, the WaitCursor class offers a lightweight, IDisposable wrapper to temporarily override the mouse cursor to a wait state and restore it upon disposal. The App class (a partial class for Application) extends this functionality at the application level by coordinating global busy/idle states: it disables the main window and manages a singleton WaitCursor instance across thread boundaries using the WPF Dispatcher, ensuring safe, re-entrant, and exception-safe busy/idle transitions—typically used before/after data-intensive operations (e.g., database refreshes or imports).
2. Public Interface
WaitCursor class (DataImport.WaitCursor)
-
public WaitCursor()
Constructor. Saves the currentMouse.OverrideCursor, setsMouse.OverrideCursor = Cursors.Wait, and prepares for later restoration viaDispose(). -
public void Dispose()
Restores the previously saved cursor (_previousCursor) toMouse.OverrideCursor. Must be called exactly once per instance (perusingor manual disposal pattern). No-op if called multiple times (but cursor may be incorrectly restored if disposed more than once).
App class (DatabaseImport.App, partial)
-
public ISO13499FileDb IsoDb { get; }
Lazy-initialized singleton accessor for theISO13499FileDbdatabase instance. On first access, instantiates_isoDb, calls_isoDb.RefreshAllData(), and returns it. Thread-safety is not guaranteed (no locking around initialization orRefreshAllData()). -
public void SetAppBusy()
Sets the application into a busy state:- Marshals to the UI thread via
Dispatcher.BeginInvokeif called from a non-UI thread. - Disposes any existing
_wc(if not null). - Creates a new
WaitCursorinstance and assigns it to_wc. - Sets
MainWindow.IsEnabled = false. - Uses
lock (WaitCursorLock)to serialize concurrent calls. - No-op if
MainWindowis null.
- Marshals to the UI thread via
-
public void SetAppAvailable()
Sets the application into an available (idle) state:- Marshals to the UI thread via
Dispatcher.BeginInvokeif called from a non-UI thread. - Disposes and nullifies
_wc(if not null). - Sets
MainWindow.IsEnabled = true. - Uses
lock (WaitCursorLock)to serialize concurrent calls. - No-op if
MainWindowis null.
- Marshals to the UI thread via
3. Invariants
- Cursor restoration guarantee: After a
WaitCursorinstance is disposed,Mouse.OverrideCursoris restored to the value it held at the time of construction. - Application busy/idle state consistency:
MainWindow.IsEnabledisfalseonly when_wcis non-null (i.e., during aSetAppBusy()call with no interveningSetAppAvailable())._wcisnullonly when the application is not busy (i.e., afterSetAppAvailable()or before anySetAppBusy()call).
- Thread-safety of state transitions:
SetAppBusy()andSetAppAvailable()are thread-safe with respect to each other vialock (WaitCursorLock).- Both methods always marshal to the UI thread via
Dispatcher.BeginInvokeif invoked off-thread, ensuring UI mutations occur on the correct thread.
- Singleton
IsoDbinitialization:_isoDbis initialized at most once, and only upon first access toIsoDb.RefreshAllData()is called exactly once per instance creation.
4. Dependencies
This module depends on:
System.Windows.Input.Cursors(forCursors.Wait)System.Windows.Input.Mouse(forMouse.OverrideCursor)System.Windows.Threading.Dispatcher(forDispatcher.CheckAccess,BeginInvoke)System.Windows.Application(base classApp : Application)System.Windows.Window(viaMainWindow)DatabaseImport.ISO13499FileDb(referenced inApp.IsoDbproperty; defined elsewhere in the codebase)
This module is depended on by:
- Any code that needs to wrap long-running operations in a wait cursor (e.g.,
using (new WaitCursor()) { ... }). - Code that triggers database refreshes or imports (uses
App.IsoDb). - UI or service layers that call
App.SetAppBusy()before andApp.SetAppAvailable()infinallyblocks (as recommended in comments).
5. Gotchas
IsoDbinitialization is not thread-safe: Multiple concurrent first-time accesses toIsoDbmay result in multipleISO13499FileDbinstances being created andRefreshAllData()being called multiple times. No locking or double-check pattern is used.WaitCursoris not re-entrant: IfWaitCursoris constructed multiple times without disposal (e.g., nestedusingblocks), only the outermost cursor state is preserved in_previousCursor. Inner disposals will overwrite the cursor prematurely.
Example:using (new WaitCursor()) // Saves cursor A, sets Wait { using (new WaitCursor()) // Saves cursor Wait, sets Wait again { // ... } // Restores cursor Wait → incorrect! } // Restores cursor ASetAppBusy()/SetAppAvailable()may silently no-op: IfMainWindowisnullat the time of call (e.g., during early startup or after window close), the methods exit early with no side effects—no exception is thrown.WaitCursorLockis static and shared across allAppinstances: SinceAppis a singleton in WPF, this is expected—but if multipleAppinstances were ever created (e.g., in tests), locking would be incorrectly shared.- No exception handling in
SetAppBusy()/SetAppAvailable(): IfMainWindow.IsEnabled = false/trueorWaitCursor.Dispose()throws, the busy/idle state may be left inconsistent (e.g.,_wcdisposed butMainWindow.IsEnablednot updated). _wcis not thread-local: Though marshaled to the UI thread,_wcis a single instance field—concurrent calls viaBeginInvokeare serialized byWaitCursorLock, but the timing of disposal/creation may cause transient cursor flicker if not used carefully (e.g., overlapping busy periods).WaitCursordoes not prevent user input beyond disablingMainWindow:Mouse.OverrideCursoralone does not block input; the disabling ofMainWindowis what prevents interaction. IfMainWindowis hidden or replaced, the busy state may persist incorrectly.
None identified beyond the above.