wpf “任何时候只能打开一个ContentDialog,“打开另一个contentdialog时出错

cetgtptt  于 2023-05-01  发布在  其他
关注(0)|答案(6)|浏览(218)

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

d7v8vwbk

d7v8vwbk1#

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

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(),如果你需要在你的应用程序代码中回调,比如你的对话框中有一个按钮,你想等待按钮按下,然后从对话框后的代码中获取值。
如果不需要等待对话框在UI代码中完成,请使用CreateContentDialog()。
使用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);
mrfwxfqh

mrfwxfqh2#

威廉·布拉德利的方法很好。只是为了润色一下,这里有一个提交并等待显示内容对话框的扩展方法;该对话框将在已经提交的所有其它内容对话框之后显示。注意:当用户点击以前积压的对话框时,你可能不再想显示你已经提交的对话框;为了表明这一点,您可以传递一个 predicate ,该 predicate 将在其他对话框被取消后进行测试。

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)。

ntjbwcob

ntjbwcob3#

“一次只能打开一个ContentDialog”
这种说法不完全正确。一次只能ShowAsync一个ContentDialog。您需要做的就是在打开另一个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();
    }
}
4xrmg8kj

4xrmg8kj4#

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

sigwle7e

sigwle7e5#

任何时候只能打开一个ContentDialog。

这是事实。(* 我真的很惊讶,但只是一会儿 )你不能有一个以上的任何时候,它更像是微软的指导方针,因为它真的很混乱,有多个对话框在对方的顶部充满了内容。
尝试更改您的UX,以只显示一个复杂的 ContentDialog,对于所有其他消息,使用MessageDialog-它支持多个按钮(
手机只有两个,但桌面上更多 )用于用户响应,但没有 * 复选框 * 或类似的“智能”内容的东西。
在我的情况下MessageDialogs真的很有帮助,但在某些领域我使用了 chained ContentDialogs,但为此,您必须
**等待 * 第一个,并在没有任何例外的情况下打开第二个。在你的例子中,当你试图打开下一个时,ContentDialog似乎没有完全关闭。
希望有帮助!

ryhaxcpt

ryhaxcpt6#

我喜欢这个答案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;
            }
        }

**修改 predicate **

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;
    }

相关问题