MahApps/MahApps.Metro · error · InvalidOperationException
Dialog isn't visible to close
Error message
Dialog isn't visible to close
What it means
Thrown by ProgressDialogController.CloseAsync when WrappedDialog.IsVisible is false. The controller is obtained from ShowProgressAsync, which already closes the dialog when its task completes or is cancelled; calling CloseAsync again finds the dialog no longer visible. It guards against double-close and orphaned event handler unsubscribes.
Source
Thrown at src/MahApps.Metro/Controls/Dialogs/ProgressDialogController.cs:174
/// Sets the dialog's progress bar brush.
/// </summary>
/// <param name="brush">The brush to use for the progress bar's foreground.</param>
public void SetProgressBarForegroundBrush(Brush brush)
{
this.WrappedDialog.Invoke(() => this.WrappedDialog.ProgressBarForeground = brush);
}
/// <summary>
/// Begins an operation to close the ProgressDialog.
/// </summary>
/// <returns>A task representing the operation.</returns>
public Task CloseAsync()
{
this.WrappedDialog.Invoke(() =>
{
if (!this.WrappedDialog.IsVisible)
{
throw new InvalidOperationException("Dialog isn't visible to close");
}
this.WrappedDialog.Dispatcher.VerifyAccess();
this.WrappedDialog.KeyDown -= this.WrappedDialog_KeyDown;
this.WrappedDialog.PART_NegativeButton!.Click -= this.PART_NegativeButton_Click;
this.cancellationTokenRegistration.Dispose();
});
return this.CloseCallback()
.ContinueWith(_ => this.WrappedDialog.Invoke(() =>
{
this.IsOpen = false;
this.Closed?.Invoke(this, EventArgs.Empty);
}));
}
}
}View on GitHub (pinned to 72099e310b)
Solutions
- Call CloseAsync exactly once; track an 'already closing' flag.
- Do not call CloseAsync if the cancellation token already closed it — let ShowProgressAsync's completion handle it.
- Guard the call: if (controller.IsOpen) await controller.CloseAsync(); — note IsOpen becomes false after close completes.
- Avoid wiring both the negative button and an explicit CloseAsync to the same dismiss path.
Example fix
// before
var controller = await this.ShowProgressAsync("...", "...");
// work done
cancellationToken.Cancel();
await controller.CloseAsync(); // may throw - cancel already closed it
// after
var controller = await this.ShowProgressAsync("...", "...");
// work done
if (controller.IsOpen)
{
await controller.CloseAsync();
} Defensive patterns
Strategy: validation
Validate before calling
// Check IsOpen before closing; it flips to false after a successful close.
if (controller.IsOpen)
{
await controller.CloseAsync();
}
// Also avoid calling CloseAsync when a cancellation token already dismisses the dialog. Try / catch
try { await controller.CloseAsync(); }
catch (InvalidOperationException ex) when (ex.Message == "Dialog isn't visible to close")
{
// Already closed (e.g. via cancel button or token). Safe to ignore.
} Prevention
- Call CloseAsync exactly once per ShowProgressAsync result.
- Do not call CloseAsync if the cancellation token or negative button already closed the dialog.
- Track a 'closing' flag to prevent concurrent close paths.
When it happens
Trigger: Calling controller.CloseAsync() twice; calling CloseAsync after the negative/cancel button already dismissed the progress dialog; calling CloseAsync after the CancellationTokenSource triggered the dialog's internal close.
Common situations: A 'cancel' handler that calls both controller.CloseAsync() and cancels the token (which itself closes the dialog); awaiting ShowProgressAsync and then also calling CloseAsync; race between a work-task completion and an explicit close.
Related errors
- The provided dialog is not visible in the specified window.
- The provided dialog is already visible in the specified wind
- OverlayBox can not be founded in this MetroWindow's template
- The inverse Flyout theme only works if the window theme abid
- The Dark Flyout theme only works if the window theme abides
AI-assisted analysis of MahApps/MahApps.Metro@72099e310b (2026-08-13).
Data as JSON: /api/errors/0680b7c9aba45315.
Report an issue: GitHub.