MahApps/MahApps.Metro · error · InvalidOperationException

Unable to find the dialog closing storyboard. Did you forget

Error message

Unable to find the dialog closing storyboard. Did you forget to add BaseMetroDialog.xaml to your merged dictionaries?

What it means

WaitForCloseAsync animates a dialog's hide using the 'MahApps.Storyboard.Dialogs.Close' resource. When DialogSettings.AnimateHide is true but TryFindResource returns nothing (the resource key is absent from the dialog's resources), it throws InvalidOperationException pointing at the missing BaseMetroDialog.xaml. The resource ships inside the MahApps.Metro dialog templates, so its absence means the templates were not merged into the application's resource dictionaries.

Source

Thrown at src/MahApps.Metro/Controls/Dialogs/BaseMetroDialog.cs:514

        {
            var tcs = new TaskCompletionSource<object>();

            this.Unloaded += (_, _) => { tcs.TrySetResult(null!); };

            return tcs.Task;
        }

        private EventHandler? closingStoryboardOnCompleted;

        public Task WaitForCloseAsync()
        {
            var tcs = new TaskCompletionSource<object>();

            if (this.DialogSettings.AnimateHide)
            {
                if (this.TryFindResource("MahApps.Storyboard.Dialogs.Close") is not Storyboard closingStoryboard)
                {
                    throw new InvalidOperationException("Unable to find the dialog closing storyboard. Did you forget to add BaseMetroDialog.xaml to your merged dictionaries?");
                }

                closingStoryboard = closingStoryboard.Clone();

                this.closingStoryboardOnCompleted = (_, _) =>
                    {
                        closingStoryboard.Completed -= this.closingStoryboardOnCompleted;

                        tcs.TrySetResult(null!);
                    };

                closingStoryboard.Completed += this.closingStoryboardOnCompleted;

                closingStoryboard.Begin(this);
            }
            else
            {
                this.SetCurrentValue(OpacityProperty, 0.0);

View on GitHub (pinned to 72099e310b)

Solutions

  1. Merge the MahApps.Metro dialog resource dictionaries into App.xaml (e.g. <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml"/>) so the storyboard key is available globally.
  2. If you use a custom dialog template, ensure it (or its merged dictionaries) defines the 'MahApps.Storyboard.Dialogs.Close' Storyboard.
  3. Alternatively set DialogSettings.AnimateHide = false to skip the animation path that needs the storyboard.
  4. Verify the correct MahApps.Metro version is referenced; the resource key exists in the shipped BaseMetroDialog.xaml.

Example fix

<!-- before: dialogs work but AnimateHide throws on close -->
<Application.Resources/>
<!-- after: merge the MahApps control/dialog resources -->
<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml"/>
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</Application.Resources>
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the closing storyboard resource is resolvable before enabling AnimateHide
bool hasCloseStoryboard = dialog.TryFindResource("MahApps.Storyboard.Dialogs.Close") is Storyboard;
dialog.DialogSettings.AnimateHide = hasCloseStoryboard;
dialog.DialogSettings.AnimateShow = hasCloseStoryboard;

Try / catch

try {
    await dialogCoordinator.HideMetroDialogAsync(context, dialog);
} catch (InvalidOperationException ex) when (ex.Message.Contains("closing storyboard")) {
    // fall back to non-animated close
    dialog.DialogSettings.AnimateHide = false;
    await dialogCoordinator.HideMetroDialogAsync(context, dialog);
}

Prevention

When it happens

Trigger: A BaseMetroDialog (or subclass) is shown with AnimateHide == true, and when it closes WaitForCloseAsync cannot find the 'MahApps.Storyboard.Dialogs.Close' Storyboard resource — typically because the MahApps.Metro dialog resource dictionaries were not merged into App.xaml or the dialog's own resources.

Common situations: App uses MahApps dialogs but forgot to merge MahApps.Metro.Resources.Dialogs (or the BaseMetroDialog template) into MergedDictionaries. A custom dialog template removed the closing storyboard. Resource cleanup/replacement dropped the key. An older MahApps version whose key name differs.

Related errors


AI-assisted analysis of MahApps/MahApps.Metro@72099e310b (2026-08-13). Data as JSON: /api/errors/5754eb4e36c50c12. Report an issue: GitHub.