2013-01-23 39 views
1

如果在另一個MessageDialog對象已經顯示給用戶但尚未解散(即當另一個已彈起時顯示彈出窗口)時調用MessageDialog對象上的ShowAsync命令,則會拋出UnauthorizedAccessException。當您有多個線程同時嘗試提醒用戶時,這可能會使事情變得困難。如何在WinRT中同時允許多個彈出窗口?

我目前的(權限管理)解決方案僅僅是用try/catch塊包圍ShowAsync調用併吞下異常。這不合需要地導致用戶在隨後的通知中錯過。我能想到的唯一方法就是手動實現某種彈出隊列。這似乎是一個過多的工作,但是,考慮到其他框架(如Windows Phone)沒有這個問題,並且只會在用戶解散它們時纔會顯示彈出窗口。

有沒有解決這個問題的另一種方法?

+0

檢查這個ou t:http://stackoverflow.com/questions/12722490/messagedialog-showasync-throws-accessdenied-exception-on-second-dialog – MUG4N

+0

謝謝,@ MUG4N,但聽起來像它關閉了初始對話框,這仍然會導致用戶錯過彈出窗口,以至於不能解決我的問題。 – jokeefe

回答

8

有很多方法可以接近它,而選擇可能取決於您的技能,要求和偏好。

我個人的選擇是完全避免使用對話框,因爲它們對用戶體驗不利(evil)。然後有可選的解決方案,例如用UI顯示單獨的屏幕/頁面,要求用戶在真正需要時提供一些輸入,或者如果用戶輸入是可選的並隱藏它,則在側面/邊緣/角落的某處顯示非模式彈出經過片刻或其他某種不會中斷用戶流量的通知。

如果您不同意或沒有時間,資源或技能來實現一個選擇 - 你可以建立某種形式的周圍MessageDialog.ShowAsync(包裝的)打電話給任一隊列或忽略新的請求,而一個對話框已經所示。

這個類擴展方法允許任何時候已經顯示另一個對話框,忽略了一個新節目請求或排隊的請求:

/// <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(); 
+0

感謝您的建議,但在我的情況下,模態對話框是必要的。 「忽略新請求」方法是我已經通過try/catch塊實現的。 「排隊」方法是我希望避免的另一個建議。 – jokeefe

+0

我添加了一些代碼來展示如何排隊請求。它確實需要你總是使用ShowAsyncQueue或ShowAsyncIfPossible而不是ShowAsync(),否則我需要捕獲異常並繼續重試這將是一個非常糟糕的設計。 –

+0

感謝代碼@filip Skakun。我最終接受了你對另一個的回答,因爲你的實際表現似乎稍好一些。 – jokeefe

11

你可以很容易用這種擴展方法做:

public static class MessageDialogShower 
{ 
    private static SemaphoreSlim _semaphore; 

    static MessageDialogShower() 
    { 
     _semaphore = new SemaphoreSlim(1); 
    } 

    public static async Task<IUICommand> ShowDialogSafely(this MessageDialog dialog) 
    { 
     await _semaphore.WaitAsync(); 
     var result = await dialog.ShowAsync(); 
     _semaphore.Release(); 
     return result; 
    } 
} 
+0

這是迄今爲止這個問題最簡單的解決方案。非常感謝你! – jokeefe

+1

這可能是微軟應該首先實現顯示'MessageDialog'的底層功能!我很高興我現在知道'SemaphoreSlim'。 – Ne0

相關問題