问题描述
如果您在 MessageDialog
对象上调用 ShowAsync
命令,而另一个 MessageDialog
对象已向用户显示但未关闭(即当另一个已经启动时显示一个弹出窗口),抛出一个 UnauthorizedAccessException
.当您有多个线程试图同时提醒用户时,这可能会使事情变得困难.
If you call the ShowAsync
command on a MessageDialog
object when another MessageDialog
object has already been displayed to the user but not dismissed (i.e. you show a popup when another one is already up), an UnauthorizedAccessException
is thrown. This can make things difficult when you have multiple threads attempting to alert the user at the same time.
我当前的(权宜之计)解决方案只是用 try/catch 块包围 ShowAsync
调用并吞下异常.这不希望地导致用户错过后续通知.我能想到的唯一其他方法是手动实现某种弹出队列.这似乎是一项过多的工作,但是,考虑到其他框架(如 Windows Phone)没有这个问题,并且只会在用户关闭它们时一个接一个地显示弹出窗口.
My current (stopgap) solution is merely to surround the ShowAsync
call with a try/catch block and swallow the exception. This undesirably leads to the user missing out on subsequent notifications. The only other way around this that I can think of is to manually implement some sort of popup queue. This seems like an inordinate amount of work, however, considering other frameworks (like Windows Phone) do not have this issue and will merely display the popups one after another as the user dismisses them.
还有其他方法可以解决这个问题吗?
Is there another way to solve this problem?
推荐答案
有很多方法可以实现,选择可能取决于您的技能、要求和偏好.
There are many ways to approach it and the choice might depend on your skills, requirements and preferences.
我个人的选择是完全避免使用对话框,因为它们不利于用户体验(邪恶).然后有替代解决方案,例如使用 UI 显示单独的屏幕/页面,要求用户在确实需要时提供一些输入,或者如果用户输入是可选的,则在侧面/边缘/角落的某处显示非模态弹出窗口并隐藏它经过片刻或其他不会破坏用户流程的通知.
My personal choice is to avoid using dialog boxes altogether since they are bad for user experience (evil). There are then alternative solutions like displaying a separate screen/page with the UI requiring user to provide some input when it really is required or displaying a non-modal popup somewhere on the side/edge/corner if the user input is optional and hiding it after a moment or some other sort of notification that doesn't break user flow.
如果您不同意或没有时间、资源或技能来实施替代方案 - 您可以围绕 MessageDialog.ShowAsync() 调用创建某种包装器,以便在已显示对话框时排队或忽略新请求.
If you disagree or don't have the time, resources or skills to implement an alternative - you can create some sort of a wrapper around MessageDialog.ShowAsync() call to either queue or ignore new requests while a dialog is already shown.
此类具有扩展方法,允许在另一个对话框已显示时忽略新的显示请求或将请求排队:
This class has extension methods to allow to either ignore a new show request when another dialog is already displayed or queue up the requests:
/// <summary>
/// MessageDialog extension methods
/// </summary>
public static class MessageDialogExtensions
{
private static TaskCompletionSource<MessageDialog> _currentDialogShowRequest;
/// <summary>
/// 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.
/// </summary>
/// <param name="dialog">The dialog.</param>
/// <returns></returns>
/// <exception cref="System.InvalidOperationException">This method can only be invoked from UI thread.</exception>
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<MessageDialog>();
await dialog.ShowAsync();
_currentDialogShowRequest = null;
request.SetResult(dialog);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="dialog">The dialog.</param>
/// <returns></returns>
/// <exception cref="System.InvalidOperationException">This method can only be invoked from UI thread.</exception>
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<MessageDialog>();
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();
这篇关于如何在 WinRT 中一次允许多个弹出窗口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!