我正在审查我的同事的一些
WPF代码,这是一个基于UserControl的组件库,带有大量异步无效事件和命令处理程序.这些方法目前在内部不执行任何错误处理.
代码简而言之:
<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中的错误.不是一个理想的解决方案,加上错误报告UI不应该始终是库代码的一部分.
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模式,以将错误从异步无效命令处理程序传播到ViewModal?
>在WPF命令处理程序中执行异步工作通常是一个坏主意,也许它们用于快速同步UI更新?
我在WPF的上下文中提出这个问题,但是我认为它也可以应用于WinForms中的async void事件处理程序.
解决方法
这里的问题是您的UserControl库没有以典型的MVVM方式架构.通常,对于不平凡的命令,您的UserControl的代码将不会直接绑定到命令,而是具有设置(通过绑定到viewmodel)将触发控件中的操作的属性.然后,您的viewmodel将绑定到应用程序命令,并设置相应的属性. (或者,您的MVVM框架可能会有另一个消息传递场景,可以用于viewmodel和View之间的交互).
对于UI中抛出的异常,我再次觉得有一个架构问题.如果UserControl做的不只是作为View,(即运行任何可能导致意外的异常的业务逻辑),那么这应该分为View和viewmodel. viewmodel将运行逻辑,可以由您的其他应用程序viewmodels实例化,也可以通过另一种方法进行通信(如上所述).
如果UserControl的布局/可视化代码抛出异常,那么这个应用程序(几乎无一例外)不会以任何方式被viewmodel所捕获.正如您所提到的,这应该仅由全局级处理程序处理以进行日志记录.
最后,如果在控件的代码中真正知道您的viewmodel需要被通知的“异常”,我建议捕获已知的异常并提出事件/命令并设置属性.但是再次,这真的不应该用于异常,只是预期的“错误”状态.