我是WPF新手,我使用的是VS2010 beta2,.NET 4.0。
我的代码中的 Throw new Exception("test")
只是吞下异常而应用程序不会崩溃。
这不是我所期望的,如果发生未处理的异常,我希望应用程序崩溃。
有没有一种简单的方法可以达到这个目的?
此外,Application.DispatcherUnhandledException
和AppDomain.UnhandledException
都未执行。这可能是因为所有代码都是作为数据绑定的一部分执行的(我使用MVVM模式并在ViewModel构造函数中抛出异常)。
调试时,我可以查看“输出”窗口,找出问题所在。但在我看来奇怪的是,该应用程序只是忽略错误,使UI处于不正确状态并且不会崩溃。
修改
似乎在数据绑定时可能只发生非关键的绑定异常。也许解决方案可能是从绑定执行中提取与绑定没有直接关系的功能(例如连接到数据库)。但是我不确定如何在MVVM中实现它。
简化示例:
XAML:
<DataTemplate DataType="{x:Type vm:ItemViewModel}">
<vw:ItemControl />
</DataTemplate>
<ContentControl
Content="{Binding Path=MyItem}"
/>
其中MyItem
创建并返回ItemViewModel
的实例。问题是ItemViewModel
的构造函数是作为数据绑定的一部分执行的,我不确定这是不是很好的做法(这个构造函数包含可能失败的代码 - 例如,如果数据库不可访问)。
答案 0 :(得分:1)
您可以捕获绑定期间引发的异常并重新抛出它们 - 请参阅the answer to this question。
答案 1 :(得分:0)
为什么会回答here和here。他们还解释了更好地调试异常的方法。但是没有关于如何将它们变成未处理的异常的答案。
编辑:您可以指示绑定在绑定更新源时发出错误信号。见here。然后,您可以使用ErrorTemplate(或使用默认值)在UI中显示错误。
{Binding Age, ValidatesOnDataErrors=true}
答案 2 :(得分:0)
我会自己回答我的问题:
通过将可能失败的代码(例如数据库访问调用)从(数据绑定)属性getter移动到View-Model的构造函数,我实现了正确的行为。另外我使用BackgroundWorker进行此调用,因此代码是异步执行的。我在RunWorkerCompleted中重新抛出可能的异常 - BackgroundWorker保证它将在UI线程上完成。
public TestViewModel()
{
BackgroundWorker bckgWorker = new BackgroundWorker();
bckgWorker.DoWork += ((s, e) => this.TestExecuteCode());
bckgWorker.RunWorkerCompleted += ((s, e) =>
{
if (e.Error != null)
throw e.Error;
});
bckgWorker.RunWorkerAsync();
}
private void TestExecuteCode()
{
this.DataBoundProperty = LoadDataFromDb();
}
我非常喜欢这种异步获取数据的方法,而不是在XAML中的Binding上设置IsAsync = true。与IsAsync方法相比,当渲染线程抛出异常并且默认情况下被吞下时,此方法永远不会吞下Exceptions。
警告:在单元测试中运行此代码时,默认情况下不会将RunWorkerCompleted事件处理程序封送回调用方的线程。必须手动设置BackgroundWorker使用的SynchronizationContext,以便正确处理单元测试中的异常。
答案 3 :(得分:0)
.Net 4.0 \ WPF介绍了这样做的方法,请参阅answer here
答案 4 :(得分:-1)
我很确定使用.NET 4的设计不佳的应用程序可能会滥用AppDomain.FirstChanceException来实现此目的。
构建日志为您提供了在WPF应用程序中查找问题所需了解的所有内容。这是我在某些代码中抛出NotImplementedException
时记录的内容,我知道UI必然会被绑定:
System.Windows.Data Error: 17 : Cannot get 'ExitCommand' value (type 'RelayCommand') from '' (type 'ControlCenter'). BindingExpression:Path=ExitCommand; DataItem='ControlCenter' (Name='controlCenterWindow'); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand') TargetInvocationException:'System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NotImplementedException: The method or operation is not implemented.
at Tvl.Client.ControlCenter.get_ExitCommand() in C:\dev\Tvl\Client\ControlCenter.xaml.cs:line 25
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at MS.Internal.Data.PropertyPathWorker.GetValue(Object item, Int32 level)
at MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'