"任何时候都只能打开一个ContentDialog。"打开另一个contentdialog

时间:2015-10-08 14:14:32

标签: c# wpf

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

6 个答案:

答案 0 :(得分:6)

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

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及其内容,然后将变量传递给方法。

使用CreateContentDialogAsync(),如果您需要在应用程序代码中进行回调,例如,如果对话框中有一个按钮,并且您希望等待按下按钮,然后在对话框后从代码中获取表单中的值。

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

使用awaitPreviousDialog在显示下一个Dialog之前等待上一个对话框完成,或者设置为false以删除上一个Dialog,然后显示下一个Dialog,例如,如果要显示错误框,或者显示下一个Dialog更重要的是。

示例:

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

答案 1 :(得分:3)

威廉布拉德利的上述方法很好。只是稍微改进一下,这是一个提交和等待显示内容对话框的扩展方法;在已经提交的所有其他内容对话框之后将显示该对话框。注意:当用户点击之前的积压对话框时,您可能不再希望显示已提交的对话框;为了表明这一点,您可以传递一个谓词,该谓词将在其他对话框被解除后进行测试。

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

答案 2 :(得分:1)

“一次只有一个ContentDialog可以打开

这种说法并不完全正确。您一次只能 ShowAsync 一个ContentDialog。您需要做的就是隐藏当前的ContentDialog,然后再打开另一个。然后,在第二个ContentDailog的“等待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();
    }
}

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

这是事实。 (我真的很惊讶,但只是片刻) 你不能在任何时候拥有多个,这更像是微软的指南,因为让多个对话框在彼此之上填充内容真的很麻烦。

尝试更改 UX 以仅显示一个复杂的 ContentDialog ,对于所有其他消息,请使用 MessageDialog - 它支持多个按钮(只有两个用于手机,但更多用于桌面)用于用户响应,但没有 Checkboxes 或类似的“智能”内容。

在我的情况下,MessageDialogs确实非常有用,但在某些方面我使用链式 ContentDialogs,但为此你必须 等待第一个,并在没有任何例外的情况下立即开放。在您的情况下,当您尝试打开下一个时,似乎ContentDialog未完全关闭。

希望它有所帮助!

答案 5 :(得分:0)

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