WinRT – MessageDialog.ShowAsync将在我的自定义类中抛出UnauthorizedAccessException

我想编写自己的控件,当调用ctor时,会显示一个MessageBox。

public class Class1 { public Class1() { ShowDialog(); } void ShowDialog() { SynchronizationContext context = SynchronizationContext.Current; if (context != null) { context.Post((f) => { MessageDialog dialog = new MessageDialog("Hello!"); dialog.ShowAsync(); }, null); } } } 

如果我的类被某人使用,并编写如下代码,则始终在dialog.ShowAsync();抛出UnauthorizedAccessException dialog.ShowAsync();

 void MainPage_Loaded(object sender, RoutedEventArgs e) { ClassLibrary1.Class1 c1 = new ClassLibrary1.Class1(); MessageDialog dialog1 = new MessageDialog(""); dialog1.ShowAsync(); } 

有没有办法毫无例外地显示消息对话框?


我找到了一种方式,享受它!

 Task ShowDialog() { CoreDispatcher dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher; Func<object, Task> action = null; action = async (o) => { try { if (dispatcher.HasThreadAccess) await new MessageDialog("Hello!").ShowAsync(); else { dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action(o)); } return true; } catch (UnauthorizedAccessException) { if (action != null) { Task.Delay(500).ContinueWith(async t => await action(o)); } } return false; }; return action(null); } 

由于MessageDialogue需要在UI线程上运行,您可以尝试将其切换为:

 var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher; dispatcher.RunAsync(DispatcherPriority.Normal, ); 

清洁解决方案可能如下所示。 有趣的部分隐藏在showDialogAsync()中。 为方便起见,您可以使用Close()方法以编程方式再次关闭当前对话框。 IUIDispatcher是另一个可以轻松重建的辅助界面:

  private readonly IUIDispatcher _dispatcher; readonly Object _queueMonitor = new object(); readonly Object _showMonitor = new object(); private IAsyncOperation _currentDialogOperation; readonly Queue _dialogQueue = new Queue(); public async Task ShowAsync(string content) { var md = new MessageDialog(content); await showDialogAsync(md); } public async Task ShowAsync(string content, string caption) { var md = new MessageDialog(content, caption); await showDialogAsync(md); } public async Task ShowAsync(string content, MessageDialogType dialogType) { var messageDialogResult = await ShowAsync(content, null, dialogType); return messageDialogResult; } public async Task ShowAsync(string content, string caption, MessageDialogType dialogType) { var result = MessageDialogResult.Ok; var md = string.IsNullOrEmpty(caption) ? new MessageDialog(content) : new MessageDialog(content, caption); switch (dialogType) { case MessageDialogType.Ok: md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonOk"], command => result = MessageDialogResult.Ok)); md.CancelCommandIndex = 0; md.DefaultCommandIndex = 0; break; case MessageDialogType.OkCancel: md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonOk"], command => result = MessageDialogResult.Ok)); md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonCancel"], command => result = MessageDialogResult.Cancel)); md.DefaultCommandIndex = 0; md.CancelCommandIndex = 1; break; case MessageDialogType.YesNo: md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonYes"], command => result = MessageDialogResult.Yes)); md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonNo"], command => result = MessageDialogResult.No)); md.DefaultCommandIndex = 0; md.CancelCommandIndex = 1; break; case MessageDialogType.YesNoCancel: md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonYes"], command => result = MessageDialogResult.Yes)); md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonNo"], command => result = MessageDialogResult.No)); md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonCancel"], command => result = MessageDialogResult.Cancel)); md.DefaultCommandIndex = 0; md.CancelCommandIndex = 1; break; default: throw new ArgumentOutOfRangeException("dialogType"); } await showDialogAsync(md); return result; } ///  /// Shows the dialogs, queued and one after the other. /// We need this as a workaround for the the UnauthorizedAcsess exception. ///  /// The message dialog. ///  async Task showDialogAsync(MessageDialog messageDialog) { //Calls this function in a separated task to avoid ui thread deadlocks. await Task.Run(async () => { lock (_queueMonitor) { _dialogQueue.Enqueue(messageDialog); } try { while (true) { MessageDialog nextMessageDialog; lock (_queueMonitor) { if (_dialogQueue.Count > 1) { Debug.WriteLine("MessageDialogService.cs | showDialogAsync | Next dialog is waiting for MessageDialog to be accessable!!"); Monitor.Wait(_queueMonitor); //unlock and wait - regains lock after waiting } nextMessageDialog = _dialogQueue.Peek(); } var showing = false; _dispatcher.Execute(async () => { try { lock (_showMonitor) { showing = true; _currentDialogOperation = nextMessageDialog.ShowAsync(); } await _currentDialogOperation; lock (_showMonitor) _currentDialogOperation = null; } catch (Exception e) { Debug.WriteLine("MessageDialogService.cs | showDialogAsync | " + e); } lock (_showMonitor) { showing = false; Monitor.Pulse(_showMonitor); //unlock and wait - regains lock after waiting } }); lock (_showMonitor) { if (showing) { Debug.WriteLine("MessageDialogService.cs | showDialogAsync | Waiting for MessageDialog to be closed!!"); //we must wait here manually for the closing of the dialog, because the dispatcher does not return a waitable task. Monitor.Wait(_showMonitor); //unlock and wait - regains lock after waiting } } Debug.WriteLine("MessageDialogService.cs | showDialogAsync | MessageDialog was closed."); return true; } } finally { //make sure we leave this in a clean state lock (_queueMonitor) { _dialogQueue.Dequeue(); Monitor.Pulse(_queueMonitor); } } }); } public void Close(string keyContent="") { try { if (keyContent.IsNullOrEmpty()) { lock (_showMonitor) { if (_currentDialogOperation == null) return; _currentDialogOperation.Cancel(); _currentDialogOperation = null; } } else { var cancel = false; lock (_queueMonitor) { if (_dialogQueue.Count == 0) return; var currentDialog = _dialogQueue.Peek(); Debug.WriteLine("MessageDialogService.cs | Close | {0}", currentDialog.Content); if (currentDialog.Content == keyContent) { cancel = true; } } if (!cancel) return; lock (_showMonitor) { if (_currentDialogOperation == null) return; _currentDialogOperation.Cancel(); _currentDialogOperation = null; } } } catch (Exception e) { Debug.WriteLine("MessageDialogService.cs | Close | " + e); } } 

我想我已经找到了。 在主线程之外的任何其他线程中创建消息框时我遇到了同样的问题。 这是C ++解决方案,但我认为你可以轻松转换它;)

  IAsyncOperation ^Op = msgbox->ShowAsync(); task( Op ).then([=](IUICommand^ C) { }).then([](task t) { try { t.get(); } catch (Platform::Exception ^e) { //ERROR! } }); 

另外,这是处理任何WinRT / Windows 8 Store C ++exception的正确方法。

你可以随时使用

 Execute.OnUIThread( async () => { ... var dialog = new MessageDialog(yourMessage); await dialog.ShowAsync(); ... }); 

如果您尝试从后台线程启动多个对话框,则无法解决UI中的竞争条件。 但您可以使用try / catch来确保覆盖该案例。