What is the proper way to show a WinRT MessageDial

2019-02-14 18:40发布

问题:

What is the proper way to show a message dialog due to a caught exception?

I originally tried

try
{
    await DoSomething();
}
catch(InvalidOperation ex)
{
    await MessageDialog(ex.Message).ShowAsync();
}
catch(CommunicationException)
{
    await MessageDialog(StringResourceLoader.LoginError).ShowAsync();
}

This did not work because you cannot await inside of a try block. Taking the await commands out makes the compiler shows the following warning:

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call

I don't like keeping those warnings in my code, because in several spots people have forgotten to use await and thus had hard to find bugs.

Changing the message dialog statement to var task = new MessageDialog(ex.Message).ShowAsync().AsTask(); gets rid of all warnings and errors, but I am not sure that is a good way to go about it (and technically is bad for the same reason it wants me to await the call)

Finally, I tried storing the exception and doing my logic of what to display to the user (including all logic on determining what type of exception was thrown) outside of the catch, via:

Exception thrownException = null;
try
{
    await DoSomething();
}
catch(Exception ex)
{
    thrownException = ex;
}

if (thrownException is InvalidOperationException)
    await MessageDialog(ex.Message).ShowAsync();
else if (thrownException is CommunicationException)
    await MessageDialog(StringResourceLoader.LoginError).ShowAsync();

I am not sure I feel that this is the best way to go about it either. Any ideas how this should be done?

回答1:

Edit: After Hans' comment about other issues, I ended up solving it by creating the following class:

public static class MessageDialogDisplayer
{
    private static readonly ConcurrentQueue<MessageDialog> _dialogQueue;
    private static readonly CancellationTokenSource _cancellationTokenSource;

    static MessageDialogDisplayer()
    {
        _dialogQueue = new ConcurrentQueue<MessageDialog>();
        _cancellationTokenSource = new CancellationTokenSource();

        new Task(DisplayQueuedDialogs, _cancellationTokenSource.Token).Start();
    }

    public static void DisplayDialog(MessageDialog dialog)
    {
        _dialogQueue.Enqueue(dialog);
    }

    private static async void DisplayQueuedDialogs()
    {
        const int millisecondsBetweenDialogChecks = 500;

        while (!_cancellationTokenSource.Token.IsCancellationRequested)
        {
            MessageDialog dialogToDisplay;
            if (_dialogQueue.TryDequeue(out dialogToDisplay))
            {
                await dialogToDisplay.ShowAsync();
            }
            else
            {
                await Task.Delay(millisecondsBetweenDialogChecks);
            }
        }
    }
}

This has changed my try/catch statement to

MessageDialog errorDialog = null;
try
{
    await DoSomething();
}
catch(InvalidOperation ex)
{
    MessageDialogDisplayer.DisplayDialog(new MessageDialog(ex.Message));
}
catch(CommunicationException)
{
    MessageDialogDisplayer.DisplayDialog(new MessageDialog(StringResourceLoader.LoginError));
}

Which makes things more stable in the long run (once I convert all message dialog callers to use this instead of showAsyncing themselves) and makes the try/catch block a lot less messy (imo).