{"record":{"id":"d0dc86600570fda8","repo":"lepoco/wpfui","slug":"isfocusinsidedialog-can-only-be-called-from-the-ui","errorCode":null,"errorMessage":"IsFocusInsideDialog can only be called from the UI thread.","messagePattern":"IsFocusInsideDialog can only be called from the UI thread\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Wpf.Ui/Controls/ContentDialog/ContentDialog.FocusBehavior.cs","lineNumber":56,"sourceCode":"    /// <summary>\n    /// Returns <see langword=\"true\"/> when the keyboard focus is currently within the dialog's visual/logical tree.\n    /// </summary>\n    /// <exception cref=\"InvalidOperationException\">\n    /// Thrown when the method is called from a non-UI thread.\n    /// </exception>\n    public bool IsFocusInsideDialog()\n    {\n        if (Dispatcher is not { HasShutdownStarted: false, HasShutdownFinished: false })\n        {\n            return false;\n        }\n\n        if (Dispatcher.CheckAccess())\n        {\n            return IsFocusInsideDialogCore(Keyboard.FocusedElement);\n        }\n\n        throw new InvalidOperationException(\"IsFocusInsideDialog can only be called from the UI thread.\");\n    }\n\n    /// <summary>\n    /// Completely prevents focus from escaping the ContentDialog. When a focus escape is detected,\n    /// the focus is forcibly pulled back into the dialog.\n    /// </summary>\n    protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)\n    {\n        if (_suppressFocusRestore)\n        {\n            return;\n        }\n\n        var window = Window.GetWindow(this);\n        if (e.NewFocus == null || window is not { IsActive: true })\n        {\n            return;\n        }","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/lepoco/wpfui/blob/ffebacd61058170cf63864b7d5aa730cffff848a/src/Wpf.Ui/Controls/ContentDialog/ContentDialog.FocusBehavior.cs#L38-L74","documentation":"Thrown by ContentDialog.IsFocusInsideDialog when the call is made on a thread other than the UI (dispatcher) thread and the dispatcher is still running. The method touches Keyboard.FocusedElement, which has UI-thread affinity, so cross-thread access is explicitly rejected.","triggerScenarios":"Invoking IsFocusInsideDialog() from a background/task thread while the dispatcher is alive (HasShutdownStarted/HasShutdownFinished false) and Dispatcher.CheckAccess() returns false (current thread does not own the dispatcher).","commonSituations":"Calling focus-related logic from an async continuation that did not marshal back to the UI thread, from a timer/background worker, or from a VM command running off-dispatcher. Dispatcher.CheckAccess returning false is the direct trigger.","solutions":["Marshal the call onto the dispatcher: await using Dispatcher.InvokeAsync(() => dialog.IsFocusInsideDialog()).","Ensure any focus logic runs in a UI-thread context (e.g. inside an event handler or a command bound to UI controls).","Before calling, gate on dialog.Dispatcher.CheckAccess() to avoid the throw and switch to Invoke when needed."],"exampleFix":"// before\nbool inside = dialog.IsFocusInsideDialog(); // called from background thread\n\n// after\nbool inside = await dialog.Dispatcher.InvokeAsync(() => dialog.IsFocusInsideDialog());","handlingStrategy":"validation","validationCode":"if (dialog.Dispatcher.CheckAccess()) { return dialog.IsFocusInsideDialog(); }\nreturn dialog.Dispatcher.Invoke(() => dialog.IsFocusInsideDialog());","typeGuard":"static bool OnUiThread(System.Windows.Threading.Dispatcher d) => d.CheckAccess();","tryCatchPattern":null,"preventionTips":["Always marshal focus logic onto the UI dispatcher.","Run focus checks inside UI-thread-bound event handlers or commands.","Gate on Dispatcher.CheckAccess() before calling thread-affine members."],"tags":["content-dialog","thread-affinity","dispatcher","focus"],"backgroundTag":null,"analyzedSha":"ffebacd61058170cf63864b7d5aa730cffff848a","analyzedAt":"2026-08-13T21:36:01.370Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}