I am new to WPF, I am using VS2010 beta2, .NET 4.0.
Throw new Exception("test")
in my code simply swallows exception and application does not crash.
This is not what I expect, I want the application to crash if unhandled exception occurs.
Is there a simple way to achieve this?
Also, neither Application.DispatcherUnhandledException
nor AppDomain.UnhandledException
is executed. It is probably because all code is executed as part of the data binding (I am using MVVM pattern and exception is thrown in ViewModel constructor).
While debugging I can look into Output window and find out what is wrong. But it seems to me odd, that application simply ignores error, leaves UI in incorrect state and does not crash.
Edit:
It seems that maybe only non-critical binding exceptions should occur while databinding. Maybe extracting functionality which is not directly related to binding (for example connecting to database) out of the binding execution can be the solution. However I am not sure how to achieve it in MVVM.
Simplified example:
XAML:
<DataTemplate DataType="{x:Type vm:ItemViewModel}">
<vw:ItemControl />
</DataTemplate>
<ContentControl
Content="{Binding Path=MyItem}"
/>
where MyItem
creates and returns instance of ItemViewModel
. Problem is that constructor of ItemViewModel
is executed as part of the data binding and I am not sure if this is good practice (this constructor contains code which can fail - for example if database is not accessible).
You can catch the exceptions raised during binding and rethrow them - see the answer to this question.
The why's are answered here and here. They also explain ways to debug the exceptions better. No answers on how to turn them into unhandled exceptions though.
edit: You can instruct bindings to signal an error when a binding gets source updated. See here. Then you can use the ErrorTemplate (or use the default) to show the error in your UI.
{Binding Age, ValidatesOnDataErrors=true}
I will answer my question myself:
I achieved proper behavior by moving code which can fail (e.g. database access call) from (data bound) property getter to the constructor of View-Model. Additionaly I have used BackgroundWorker for this call, so code is executed asynchronously. I rethrow possible exception in RunWorkerCompleted - BackgroundWorker guarantees that it will be done on UI thread.
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();
}
I strongly prefer this approach of getting data asynchronously rather than IsAsync=true setting on Binding in XAML. This approach never swallows Exceptions compared to IsAsync approach when exceptions are thrown on render thread and are swallowed by default.
Warning: When running this code as part of unit tests, RunWorkerCompleted event handler will not be marshaled back to caller's thread by default. SynchronizationContext used by BackgroundWorker must be set manually in order to handle exceptions in unit tests correctly.
.Net 4.0\WPF introduces a way of doing this see answer here
I'm pretty sure a poorly designed application using .NET 4 could abuse AppDomain.FirstChanceException for this purpose.
The build log gives you everything you need to know for finding problems in your WPF application. Here's what was logged when I threw a NotImplementedException
in some code I knew the UI was bound to:
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)'