using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Sonex.Data.Database; using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace Sonex.Client.Dialogs { public static class MessageDialog { private const int DialogAlreadyOpenHResult = unchecked((int)0x80000019); private static readonly TimeSpan DialogRetryDelay = TimeSpan.FromMilliseconds(200); private static readonly SemaphoreSlim DialogLock = new(1, 1); public static async Task ShowMessageAsync(this FrameworkElement element, DB.Result? result) { if (result == null || !result.Success) await ShowMessageAsync(element, "Error", result?.ErrorMessage ?? ""); } public static async Task ShowMessageAsync(this FrameworkElement element, string title, string content) { await ShowDialogAsync( element, title, content, closeButtonText: "OK"); } public static Task ShowQuestionAsync( this FrameworkElement element, string title, string content, string primaryButtonText, string closeButtonText, string? secondaryButtonText = null) { ArgumentException.ThrowIfNullOrWhiteSpace(primaryButtonText); ArgumentException.ThrowIfNullOrWhiteSpace(closeButtonText); return ShowDialogAsync( element, title, content, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, closeButtonText: closeButtonText); } public static Task ShowMessageAsync(this Window window, string title, string content) { ArgumentNullException.ThrowIfNull(window); if (window.Content is not FrameworkElement root) throw new InvalidOperationException("Window.Content is not a FrameworkElement."); return root.ShowMessageAsync(title, content); } public static Task ShowQuestionAsync( this Window window, string title, string content, string primaryButtonText, string closeButtonText, string? secondaryButtonText = null) { ArgumentNullException.ThrowIfNull(window); if (window.Content is not FrameworkElement root) throw new InvalidOperationException("Window.Content is not a FrameworkElement."); return root.ShowQuestionAsync( title, content, primaryButtonText, closeButtonText, secondaryButtonText); } private static async Task ShowDialogAsync( FrameworkElement element, string title, string content, string? primaryButtonText = null, string? secondaryButtonText = null, string? closeButtonText = null) { ArgumentNullException.ThrowIfNull(element); await DialogLock.WaitAsync(); try { while (true) { var xamlRoot = element.XamlRoot; if (xamlRoot == null) return ContentDialogResult.None; var dialog = new ContentDialog { XamlRoot = xamlRoot, Title = title, Content = CreateDialogContent( content, isSelectableContent: string.IsNullOrWhiteSpace(primaryButtonText) && string.IsNullOrWhiteSpace(secondaryButtonText)), DefaultButton = GetDefaultButton(primaryButtonText, secondaryButtonText, closeButtonText) }; if (!string.IsNullOrWhiteSpace(primaryButtonText)) dialog.PrimaryButtonText = primaryButtonText; if (!string.IsNullOrWhiteSpace(secondaryButtonText)) dialog.SecondaryButtonText = secondaryButtonText; if (!string.IsNullOrWhiteSpace(closeButtonText)) dialog.CloseButtonText = closeButtonText; try { return await dialog.ShowAsync(); } catch (COMException ex) when (ex.HResult == DialogAlreadyOpenHResult) { await Task.Delay(DialogRetryDelay); } } } finally { DialogLock.Release(); } } private static ContentDialogButton GetDefaultButton( string? primaryButtonText, string? secondaryButtonText, string? closeButtonText) { if (!string.IsNullOrWhiteSpace(closeButtonText)) return ContentDialogButton.Close; if (!string.IsNullOrWhiteSpace(primaryButtonText)) return ContentDialogButton.Primary; if (!string.IsNullOrWhiteSpace(secondaryButtonText)) return ContentDialogButton.Secondary; return ContentDialogButton.Primary; } private static object CreateDialogContent(string? content, bool isSelectableContent) { var normalizedContent = content ?? string.Empty; if (isSelectableContent) { var textBlock = new TextBlock { Text = normalizedContent, TextWrapping = TextWrapping.Wrap, IsTextSelectionEnabled = true }; return new ScrollViewer { Content = textBlock, MinWidth = 560, MaxWidth = 980, MinHeight = 100, MaxHeight = 520, HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, VerticalScrollBarVisibility = ScrollBarVisibility.Auto }; } return new TextBlock { Text = normalizedContent, TextWrapping = TextWrapping.Wrap, IsTextSelectionEnabled = true, MaxWidth = 820 }; } } }