3

別のオブジェクトが既にユーザーに表示されているが閉じられていないときにオブジェクトでShowAsyncコマンドを呼び出すと(つまり、別のオブジェクトが既に表示されているときにポップアップを表示する)、がスローされます。これにより、複数のスレッドが同時にユーザーに警告しようとしている場合、事態が難しくなる可能性があります。MessageDialogMessageDialogUnauthorizedAccessException

私の現在の (一時しのぎの) 解決策は、単にShowAsync呼び出しを try/catch ブロックで囲み、例外を飲み込むことです。これにより、ユーザーはその後の通知を見逃してしまうことになり、望ましくありません。これを回避する唯一の方法は、ある種のポップアップ キューを手動で実装することです。これは途方もない量の作業のように思えますが、他のフレームワーク (Windows Phone など) にはこの問題がなく、ユーザーがポップアップを閉じたときにポップアップを次々に表示するだけであることを考えると.

この問題を解決する別の方法はありますか?

4

2 に答える 2

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;
    }
}
于 2013-01-23T22:15:56.413 に答える
10

アプローチには多くの方法があり、選択はスキル、要件、好みによって異なる場合があります。

私の個人的な選択は、ユーザー エクスペリエンス (悪)に悪いので、ダイアログ ボックスを完全に使用しないことです。次に、別の画面/ページを表示して、実際に必要なときにユーザーに入力を要求する 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();
于 2013-01-23T20:59:11.803 に答える