11

Desidero scrivere il mio controllo personale, quando viene richiamato il ctor, viene visualizzato un MessageBox.WinRT - MessageDialog.ShowAsync genererà UnauthorizedAccessException nella mia classe personalizzata

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

Se la mia classe è utilizzata da qualcuno, e scrivere i codici come sotto, UnauthorizedAccessException è sempre gettato in dialog.ShowAsync();

void MainPage_Loaded(object sender, RoutedEventArgs e) 
     { 

      ClassLibrary1.Class1 c1 = new ClassLibrary1.Class1(); 
      MessageDialog dialog1 = new MessageDialog(""); 
      dialog1.ShowAsync(); 
     } 

C'è un modo per mostrare un messaggio di dialogo senza eccezione?


Ho trovato un modo, divertiti!

Task ShowDialog() 
     { 
      CoreDispatcher dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher; 
      Func<object, Task<bool>> 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); 
     } 
+1

Beh, è ​​doloroso. Non è possibile mostrare una finestra di messaggio quando l'interfaccia utente ne mostra già una. Questo deve essere intrecciato, molto difficile da fare, naturalmente. Nessuna vera cura, ma evitare che i thread di lavoro spingano da soli le scatole. –

+0

Ho trovato un modo, basta modificare il metodo ShowDialog come soluzione alternativa – glover

+0

Beh, non è una soluzione reale. Cosa succede al codice che non hai scritto chiama Call ShowAsync(). Ancora un kaboom. –

risposta

3

Come MessageDialogue deve essere eseguito sul thread dell'interfaccia utente, si può provare a passare a:

var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher; 
dispatcher.RunAsync(DispatcherPriority.Normal, <lambda for your code which should run on the UI thread>); 
+0

Non funziona. In effetti, l'effetto è lo stesso di SynchronizationContext.Post o Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync. Tutti sono usati per mostrare la finestra di dialogo in UI Thread. – glover

0

Penso di aver trovato. Ho avuto lo stesso problema durante la creazione di messagebox in altri thread oltre al thread principale. Questa è la soluzione C++, ma penso che è possibile convertire facilmente;)

IAsyncOperation<IUICommand^> ^Op = msgbox->ShowAsync(); 
    task<IUICommand^>(Op).then([=](IUICommand^ C) 
    { 

    }).then([](task<void> t) 
     { 
      try 
      { 
       t.get(); 
      } 
      catch (Platform::Exception ^e) 
      { 
       //ERROR!        
      }   
     }); 

Su un lato nota questo è il modo corretto di gestire qualsiasi deposito C++ eccezione WinRT/Windows 8.

0

È sempre possibile utilizzare

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

Questo non risolve condizioni di gara nell'interfaccia utente, se si sta cercando di lanciare più dialoghi dai thread in background. Ma potresti usare un try/catch per assicurarti di coprire per quel caso.

1

La soluzione di pulizia potrebbe essere simile a questa. La parte interessante è nascosta in die showDialogAsync(). Per comodità è possibile utilizzare il metodo Close() per chiudere di nuovo la finestra di dialogo corrente. L'IUIDispatcher è un'altra interfaccia di supporto che puoi ricostruire facilmente:

private readonly IUIDispatcher _dispatcher; 
    readonly Object _queueMonitor = new object(); 
    readonly Object _showMonitor = new object(); 
    private IAsyncOperation<IUICommand> _currentDialogOperation; 
    readonly Queue<MessageDialog> _dialogQueue = new Queue<MessageDialog>(); 


    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<MessageDialogResult> ShowAsync(string content, MessageDialogType dialogType) 
    { 
     var messageDialogResult = await ShowAsync(content, null, dialogType); 
     return messageDialogResult; 
    } 

    public async Task<MessageDialogResult> 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; 
    } 


    /// <summary> 
    /// Shows the dialogs, queued and one after the other. 
    /// We need this as a workaround for the the UnauthorizedAcsess exception. 
    /// </summary> 
    /// <param name="messageDialog">The message dialog.</param> 
    /// <returns></returns> 
    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); 
     } 

    }