任何时候只能打开一个ContentDialog.打开另一个内容对话框时出错

本文关键字:另一个 对话框 出错 ContentDialog 任何时候 一个 | 更新日期: 2023-09-27 18:16:43

我使用Windows.UI.Xaml.Controls.ContentDialog来显示确认。根据第一个对话框的响应,我将(或不)显示另一个对话框。但是,当我试图打开第二个内容对话框时,它抛出:"只有一个ContentDialog可以在任何时候打开。"错误。即使在UI中,第一个对话框将被关闭,但不知何故我仍然无法打开第二个对话框。任何想法?

任何时候只能打开一个ContentDialog.打开另一个内容对话框时出错

我已经创建了一些代码来处理我的应用程序中的这种难题:

public static class ContentDialogMaker
{
    public static async void CreateContentDialog(ContentDialog Dialog, bool awaitPreviousDialog) { await CreateDialog(Dialog, awaitPreviousDialog); }
    public static async Task CreateContentDialogAsync(ContentDialog Dialog, bool awaitPreviousDialog) { await CreateDialog(Dialog, awaitPreviousDialog); }
    static async Task CreateDialog(ContentDialog Dialog, bool awaitPreviousDialog)
    {
        if (ActiveDialog != null)
        {
            if (awaitPreviousDialog)
            {
                await DialogAwaiter.Task;
                DialogAwaiter = new TaskCompletionSource<bool>();
            }
            else ActiveDialog.Hide();
        }
        ActiveDialog = Dialog;
        ActiveDialog.Closed += ActiveDialog_Closed;
        await ActiveDialog.ShowAsync();
        ActiveDialog.Closed -= ActiveDialog_Closed;
    }
    public static ContentDialog ActiveDialog;
    static TaskCompletionSource<bool> DialogAwaiter = new TaskCompletionSource<bool>();
    private static void ActiveDialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs args) { DialogAwaiter.SetResult(true); }
}

要使用这些方法,您需要在一个变量中创建ContentDialog及其内容,然后将该变量和bool值传递给方法。

使用CreateContentDialogAsync(),如果你需要一个回调在你的应用程序代码,说,如果你有一个按钮在你的对话框,你想等待一个按钮按下,然后从窗体在对话框后的代码中获取值。

使用CreateContentDialog(),如果你不需要等待对话框完成在你的UI代码。

使用awaitPreviousDialog等待前一个对话框完成后再显示下一个对话框,或者设置false,删除前一个对话框,然后显示下一个对话框,比如,如果你想显示一个错误框,或者下一个对话框更重要。

的例子:

await ContentDialogMaker.CreateContentDialogAsync(new ContentDialog
{
    Title = "Warning",
    Content = new TextBlock
    {
        Text = "Roaming Appdata Quota has been reached, if you are seeing this please let me know via feedback and bug reporting, this means that any further changes to data will not be synced across devices.",
        TextWrapping = TextWrapping.Wrap
    },
    PrimaryButtonText = "OK"
}, awaitPreviousDialog: true);

William Bradley的方法很好。为了稍微润色一下,这里有一个扩展方法来提交并等待内容对话框的显示;该对话框将在已经提交的所有其他内容对话框之后显示。注意:当用户点击之前积压的对话框时,你可能不想再显示你提交的对话框;为了表明这一点,您可以传递一个谓词,该谓词将在其他对话框被驳回后进行测试。

static public class ContentDialogExtensions
{
    static public async Task<ContentDialogResult> EnqueueAndShowIfAsync( this ContentDialog contentDialog, Func<bool> predicate = null)
    {
        TaskCompletionSource<Null> currentDialogCompletion = new TaskCompletionSource<Null>();
        TaskCompletionSource<Null> previousDialogCompletion = null;
        // No locking needed since we are always on the UI thread.
        if (!CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess) { throw new NotSupportedException("Can only show dialog from UI thread."); }
        previousDialogCompletion = ContentDialogExtensions.PreviousDialogCompletion;
        ContentDialogExtensions.PreviousDialogCompletion = currentDialogCompletion;
        if (previousDialogCompletion != null) {
            await previousDialogCompletion.Task;
        }
        var whichButtonWasPressed = ContentDialogResult.None;
        if (predicate == null || predicate()) {
            whichButtonWasPressed = await contentDialog.ShowAsync();
        }
        currentDialogCompletion.SetResult(null);
        return whichButtonWasPressed;
    }
    static private TaskCompletionSource<Null> PreviousDialogCompletion = null;
}
另一种方法是使用SemaphoreSlim(1,1)。

"一次只能打开一个 "

这个说法并不完全正确。你一次只能ShowAsync一个ContentDialog。所有你需要做的是隐藏当前ContentDialog打开另一个之前。然后,在第二个ContentDailog的"await ShowAsync"之后,您只需调用"var T = this.ShowAync()"来取消隐藏它。例子:

public sealed partial class MyDialog2 : ContentDialog
{
    ...
}
public sealed partial class MyDialog1 : ContentDialog
{
    ...
    private async void Button1_Click(object sender, RoutedEventArgs e)
    {
        // Hide MyDialog1
        this.Hide(); 
        // Show MyDialog2 from MyDialog1
        var C = new MyDialog2();
        await C.ShowAsync();
        // Unhide MyDialog1
        var T = ShowAsync();
    }
}

我知道这有点老了,但一个更简单的解决方案,而不是经历所有这些痛苦是注册一个回调ContentDialog_Closed事件。此时,您可以确定前一个对话框已经关闭,并且可以打开下一个对话框。:)

每次只能打开一个ContentDialog

那是事实。(我真的很惊讶,但只是一瞬间)你在任何时候都不能有多个对话框,这更像是微软的指导方针,因为在彼此的顶部有多个充满内容的对话框真的很混乱。

尝试改变你的UX只显示一个复杂的ContentDialog和所有其他消息使用MessageDialog -它支持多个按钮(只有两个手机,但更多的桌面)的用户响应,但没有复选框或类似的"智能"内容的东西。

在我的情况下,MessageDialogs真的很有帮助,但在某些领域,我使用链接 ContentDialogs,但对于你必须等待第一个,并在没有任何例外的情况下打开第二个。在你的情况下,当你试图打开下一个ContentDialog时,它似乎没有完全关闭。

希望有帮助!

我喜欢这个答案https://stackoverflow.com/a/47986634/942855,这将允许我们处理绑定所有事件。

所以稍微扩展了一下,以检查显示对话框的多个调用。

 private int _dialogDisplayCount;
 private async void Logout_OnClick(object sender, RoutedEventArgs e)
        {
            try
            {    
                _dialogDisplayCount++;
                ContentDialog noWifiDialog = new ContentDialog
                {
                    Title = "Logout",
                    Content = "Are you sure, you want to Logout?",
                    PrimaryButtonText = "Yes",
                    CloseButtonText = "No"
                };
                noWifiDialog.PrimaryButtonClick += ContentDialog_PrimaryButtonClick;
                //await noWifiDialog.ShowAsync();
                await noWifiDialog.EnqueueAndShowIfAsync(() => _dialogDisplayCount);
            }
            catch (Exception exception)
            {
                _rootPage.NotifyUser(exception.ToString(), NotifyType.DebugErrorMessage);
            }
            finally
            {
                _dialogDisplayCount = 0;
            }
        }

修改谓词

public class Null { private Null() { } }
    public static class ContentDialogExtensions
    {
        public static async Task<ContentDialogResult> EnqueueAndShowIfAsync(this ContentDialog contentDialog, Func<int> predicate = null)
        {
            TaskCompletionSource<Null> currentDialogCompletion = new TaskCompletionSource<Null>();
            // No locking needed since we are always on the UI thread.
            if (!CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess) { throw new NotSupportedException("Can only show dialog from UI thread."); }
            var previousDialogCompletion = _previousDialogCompletion;
            _previousDialogCompletion = currentDialogCompletion;
            if (previousDialogCompletion != null)
            {
                await previousDialogCompletion.Task;
            }
            var whichButtonWasPressed = ContentDialogResult.None;
            if (predicate == null || predicate() <=1)
            {
                whichButtonWasPressed = await contentDialog.ShowAsync();
            }
            currentDialogCompletion.SetResult(null);
            return whichButtonWasPressed;
        }
        private static TaskCompletionSource<Null> _previousDialogCompletion;
    }