Я просматриваю код WPF моих коллег, который представляет собой библиотеку компонентов UserControl
с большим количеством обработчиков событий и команд async void
, В настоящее время эти методы не реализуют обработку ошибок внутри.
Код в двух словах:
<Window.CommandBindings>
<CommandBinding
Command="ApplicationCommands.New"
Executed="NewCommand_Executed"/>
</Window.CommandBindings>
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// do some fake async work (and may throw if timeout < -1)
var timeout = new Random(Environment.TickCount).Next(-100, 100);
await Task.Delay(timeout);
}
Исключения, вызванные, но не наблюдаемые внутри NewCommand_Executed
, могут обрабатываться только на глобальном уровне (например, с AppDomain.CurrentDomain.UnhandledException
). По-видимому, это не очень хорошая идея.
Я мог бы обрабатывать исключения локально:
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
try
{
// do some fake async work (throws if timeout < -1)
var timeout = new Random(Environment.TickCount).Next(-100, 100);
await Task.Delay(timeout);
}
catch (Exception ex)
{
// somehow log and report the error
MessageBox.Show(ex.Message);
}
}
Однако в этом случае приложение-хозяин ViewModel не будет знать об ошибках внутри NewCommand_Executed
. Не идеальное решение, а также пользовательский интерфейс, сообщающий об ошибках, не всегда должен быть частью кода библиотеки.
Другой подход заключается в том, чтобы обрабатывать их локально и запускать выделенное событие ошибки:
public class AsyncErrorEventArgs: EventArgs
{
public object Sender { get; internal set; }
public ExecutedRoutedEventArgs Args { get; internal set; }
public ExceptionDispatchInfo ExceptionInfo { get; internal set; }
}
public delegate void AsyncErrorEventHandler(object sender, AsyncErrorEventArgs e);
public event AsyncErrorEventHandler AsyncErrorEvent;
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
ExceptionDispatchInfo exceptionInfo = null;
try
{
// do some fake async work (throws if timeout < -1)
var timeout = new Random(Environment.TickCount).Next(-100, 100);
await Task.Delay(timeout);
}
catch (Exception ex)
{
// capture the error
exceptionInfo = ExceptionDispatchInfo.Capture(ex);
}
if (exceptionInfo != null && this.AsyncErrorEvent != null)
this.AsyncErrorEvent(sender, new AsyncErrorEventArgs {
Sender = this, Args = e, ExceptionInfo = exceptionInfo });
}
Мне больше нравится последний, но я был бы признателен за любые другие предложения, поскольку мой опыт работы с WPF несколько ограничен.
-
Есть ли установленный шаблон WPF для распространения ошибок из обработчиков команд
async void
на ViewModal? -
Как правило, плохая идея делать асинхронную работу внутри обработчиков команд WPF, поскольку, возможно, они предназначены для быстрого синхронного обновления пользовательского интерфейса?
Я задаю этот вопрос в контексте WPF, но я думаю, что он может также применяться к обработчикам событий async void
в WinForms.