如何在WinRT中同时允许多个弹出窗口?

如果在MessageDialog对象上调用ShowAsync命令时,如果另一个MessageDialog对象已经显示给用户但未被解除(即当另一个已经启动时显示弹出窗口),则抛出UnauthorizedAccessException 。 当您有多个线程试图同时提醒用户时,这会使事情变得困难。

我当前的(权宜之计)解决方案只是用try / catch块包围ShowAsync调用并吞下exception。 这不合期望地导致用户错过后续通知。 我能想到的另一种方法是手动实现某种弹出队列。 这似乎是一项过多的工作,但是,考虑到其他框架(如Windows Phone)没有这个问题,并且只会在用户解雇时一个接一个地显示弹出窗口。

有没有其他方法可以解决这个问题?

有很多方法可以接近它,选择可能取决于您的技能,要求和偏好。

我个人的选择是完全避免使用对话框,因为它们对用户体验不利( 邪恶 )。 然后有替代解决方案,例如显示单独的屏幕/页面,UI需要用户在真正需要时提供一些输入;如果用户输入是可选的并且隐藏它,则在侧面/边缘/角落的某处显示非模态弹出窗口片刻之后或其他一些不会破坏用户流量的通知。

如果您不同意或没有时间,资源或技能来实现替代方案 – 您可以在MessageDialog.ShowAsync()调用任何队列时创建某种包装,或者在已显示对话框时忽略新请求。

此类具有扩展方法,允许在已显示另一个对话框时忽略新的显示请求或对请求进行排队:

 ///  /// MessageDialog extension methods ///  public static class MessageDialogExtensions { private static TaskCompletionSource _currentDialogShowRequest; ///  /// Begins an asynchronous operation showing a dialog. /// If another dialog is already shown using /// ShowAsyncQueue or ShowAsyncIfPossible method - it will wait /// for that previous dialog to be dismissed before showing the new one. ///  /// The dialog. ///  /// This method can only be invoked from UI thread. public static async Task ShowAsyncQueue(this MessageDialog dialog) { if (!Window.Current.Dispatcher.HasThreadAccess) { throw new InvalidOperationException("This method can only be invoked from UI thread."); } while (_currentDialogShowRequest != null) { await _currentDialogShowRequest.Task; } var request = _currentDialogShowRequest = new TaskCompletionSource(); await dialog.ShowAsync(); _currentDialogShowRequest = null; request.SetResult(dialog); } ///  /// Begins an asynchronous operation showing a dialog. /// If another dialog is already shown using /// ShowAsyncQueue or ShowAsyncIfPossible method - it will wait /// return immediately and the new dialog won't be displayed. ///  /// The dialog. ///  /// This method can only be invoked from UI thread. public static async Task ShowAsyncIfPossible(this MessageDialog dialog) { if (!Window.Current.Dispatcher.HasThreadAccess) { throw new InvalidOperationException("This method can only be invoked from UI thread."); } while (_currentDialogShowRequest != null) { return; } var request = _currentDialogShowRequest = new TaskCompletionSource(); await dialog.ShowAsync(); _currentDialogShowRequest = null; request.SetResult(dialog); } } 

测试

 // This should obviously be displayed var dialog = new MessageDialog("await ShowAsync", "Dialog 1"); await dialog.ShowAsync(); // This should be displayed because we awaited the previous request to return dialog = new MessageDialog("await ShowAsync", "Dialog 2"); await dialog.ShowAsync(); // All other requests below are invoked without awaiting // the preceding ones to complete (dialogs being closed) // This will show because there is no dialog shown at this time dialog = new MessageDialog("ShowAsyncIfPossible", "Dialog 3"); dialog.ShowAsyncIfPossible(); // This will not show because there is a dialog shown at this time dialog = new MessageDialog("ShowAsyncIfPossible", "Dialog 4"); dialog.ShowAsyncIfPossible(); // This will show after Dialog 3 is dismissed dialog = new MessageDialog("ShowAsyncQueue", "Dialog 5"); dialog.ShowAsyncQueue(); // This will not show because there is a dialog shown at this time (Dialog 3) dialog = new MessageDialog("ShowAsyncIfPossible", "Dialog 6"); dialog.ShowAsyncIfPossible(); // This will show after Dialog 5 is dismissed dialog = new MessageDialog("ShowAsyncQueue", "Dialog 7"); dialog.ShowAsyncQueue(); // This will show after Dialog 7 is dismissed dialog = new MessageDialog("ShowAsyncQueue", "Dialog 8"); dialog.ShowAsyncQueue(); 

您可以使用此扩展方法轻松完成:

 public static class MessageDialogShower { private static SemaphoreSlim _semaphore; static MessageDialogShower() { _semaphore = new SemaphoreSlim(1); } public static async Task ShowDialogSafely(this MessageDialog dialog) { await _semaphore.WaitAsync(); var result = await dialog.ShowAsync(); _semaphore.Release(); return result; } }