This article covers many techniques you can use to troubleshoot and debug your WPF application, including binding errors, exception handling, inspecting the VisualTree and logging.
Table of Contents
- Introduction
- Binding Errors
- 1. Changing Trace Level for All in Options
- 2. Changing Trace Level on a Specific Binding
- WPF Tree Visualizer
- Other inspection tools
- Debuging Your Code
- Inner Exceptions
- Unhandled Exceptions
- Application.DispatcherUnhandledException
- AppDomain.CurrentDomain.UnhandledException
- TaskScheduler.UnobservedTaskException
- The catch-all Unhandled Handler!
- Drilling down to include the Inner Exceptions
- Commenting Out and Binary Chopping
- Is It Reproducable?
- Logging
- Logging Level
- Repository Choice
- Why Reinvent the Wheel
- Log4Net
- ELMAH
- NLog
- NuGet
- See Also
- Community Resources
Introduction
Windows Presentation Foundation (WPF) is a joy to develop rich user experiences with. Animations, bindings, reskinnable codeless interfaces, all backed up with traditional .Net programming all make for a very rapid development cycle. However, when something
goes wrong in that autogenerated super-sexy front-end, it is not always clear where the problem is.
This article will cover some of the methods for analyzing your WPF application, troubleshooting issues, and debugging seemingly obscure errors.
↑
Return to Top
Binding Errors
When an error occurs in the binding framework, the notification is usually «silent», in that is doesn’t pop up an error dialog to the user. The error is instead displayed in the
Output Window of Visual Studio. Although the errors seem obscure at first, there are only a few common ones and they are quite helpful messages.
Below are some typical errors and an explanation for each.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=PartIds; DataItem=null; target element is 'ComboBox'
(Name=''); target property is 'ItemsSource' (type 'IEnumerable')
This is a ComboBox inside a DataGridTemplateColumn. I am binding its ItemsSource to a property called PartIds.
It is erroring when looking for the binding source, in other words the physical object to find the property on.
In this example, the error was because the source is a Window, not a UserControl.
"System.Windows.Data Error: 40 : BindingExpression path error: 'MyStringOptionz' property not found on 'object' ''MainWindow' (Name='Window')'. BindingExpression:Path=MyStringOptionz; DataItem='MainWindow' (Name='Window'); target element is 'ComboBox'
(Name=''); target property is 'ItemsSource' (type 'IEnumerable')"
This is when the property name in the binding expression is wrong, or the property does not exist in the source object.
In this example, the property name was misspelled with a ‘z’.
Sometimes you will need a more detailed debugging output for binding. There are two ways to do this.
1. Changing Trace Level for All in Options
Click the Visual Studio menu Tools / Options, then open
Debugging and Output Window. In there you can change the
DataBinding output settings to Verbose or
All.
(click to enlarge)
2. Changing Trace Level on a Specific Binding
If you increase the trace level that way, you will get a WHOLE LOAD of messages for everything. Far better to target the specific binding where the error is. To do this, you can call on an Attached Property PresentationSources.TraceLevel and set it for just
the one binding.
<
ComboBox
ItemsSource
=
"{Binding PartIdz,
PresentationTraceSources.TraceLevel=High, RelativeSource={RelativeSource AncestorType=Window}}"
. . . />
For more information on the Trace Levels and what they represent,
read here.
↑
Return to Top
WPF Tree Visualizer
The WPF Tree Visualizer comes built into Visual Studio, and is an essential tool to know about.
With it, you can break into the Visual Tree and inspect the actual generated elements and their properties.
To use a Visualizer, you click on the magnifying glass next to a variable name in
DataTips, a Watch window, or in the
Autos, Locals, or
Quick Watch window.
If you are visualizing a string object for example, you get a big grey text window. However, if you visualize a FrameworkElement, you get the WPF Tree Visualizer.
(click to enlarge)
For a Quick Start on how to use the WPF TV, try this :
Debugging WPF — WPF Tree Visualizer + Helper Class
It also supplies a handy Attached Property you can drop in anywhere, without messing your code.
The WPF Tree Visualizer is great for inspecting the VisualTree, but if you want to
change any of the properties, you have to look at the «Locals» tab, as shown below.
(click to enlarge)
Other inspection tools
WPF Tree Visualizer is usually enough for the experienced developer to find the problem, however there are plenty of tools around if you prefer to defer.
- WPF Inspector
- UI Spy (in Microsoft Windows SDK)
- Snoop
- XamlIT
- Mole
- Pistachio
↑
Return to Top
Debuging Your Code
Apart from the WPF Tree Visualiser, the debugger in WPF applications is the main way to break into your application, trace through and follow program execution to the point of failure.
This is all standard Visual Studio stuff, and I won’t waste time here reproducing what is available elsewhere:
- Debugging in Visual Studio
- Debugging Managed Code
- Debugging, Tracing, and Profiling
↑
Return to Top
Inner Exceptions
When your application raises an exception, and you are debugging in Visual Studio, you get the usual error dialog with the details. However, when the error is a XAML error, it is most frequently buried deeper in the exception details.
Within each exception is an Inner Exception, which is just another exception, but from a layer further down. If XAML throws an error about a missing resource, it re-raises the error as a meaningless «XAML Markup error». When you drill down deeper, you find
what resource the actual error relates to.
Josh Smith wrote best documents the joy of inner exceptions here.
↑
Return to Top
Unhandled Exceptions
It is impossible to protect against every possible fault that could happen, when your application is «out in the wild».
There are however, ways to try and improve on the standard Windows error dialogue, and stack dump, which will help you find the fault.
When an exception is unhandled by your application, there are several Unhandled Exception events that you can tap into and handle.
You can download and test all five handlers here :
Handling Unhandled Exceptions in WPF (The most complete collection of handlers)
Application.DispatcherUnhandledException
This is triggered when the UI thread causes an unhandled exception. You can attach a handler to it either in App.xaml
<
Application
x:Class
=
"MyProject.App"
StartupUri
=
"MainWindow.xaml"
DispatcherUnhandledException
=
"App_DispatcherUnhandledException"
>
<
Application.Resources
/>
</
Application
>
… or App.xaml.cs
namespace
MyProject
{
public
partial
classApp : Application
{
public
App()
{
DispatcherUnhandledException +=
new
System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(App_DispatcherUnhandledException);
}
What you get in the handler arguments is a standard exception. Handle as you like, preferably write it to a permissions safe file location.
You can also set e.Handled to true if you want to try to overcome the error and move on
void
App_DispatcherUnhandledException(
object
sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
ProcessError(e.Exception);
e.Handled =
true
;
}
Sometimes the error is too severe, and your application still crashes. But at least you have hopefully captured something useful, before it did.
AppDomain.CurrentDomain.UnhandledException
If the error is not on the UI thread, then the previous handler won’t catch it. It is solely for the Dispatcher thread. To capture errors on any other thread, you need this second one.
This handler is attached in the Startup event handler, which can again be hooked into from mark-up:
<
Application
x:Class
=
"UnhandledExceptionHandler.App"
StartupUri
=
"MainWindow.xaml"
Startup
=
"App_Startup"
>
<
Application.Resources
/>
</
Application
>
… or in code:
namespace
MyProject
{
public
partial
classApp : Application
{
public
App()
{
Startup +=
new
StartupEventHandler(App_Startup);
}
void
App_Startup(
object
sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
void
CurrentDomain_UnhandledException(
object
sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject
as
Exception;
ProcessError(exception);
if
(e.IsTerminating)
MessageBox.Show(
"Goodbye world!"
);
}
Notice this uses an ExceptionObject, which is derived from Exception, so we can simply cast it back, for a generic exception handler, as shown below.
Also notice the ExceptionObject has an IsTerminating flag.
TaskScheduler.UnobservedTaskException
This exception happens when a Task has an «unobserved exception», and is about to trigger the «exception escalation policy», which by default
terminates the process.
namespace
MyProject
{
public
partial
classApp : Application
{
public
App()
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
Again, we can try to prevent crashing, in this case by calling the e.SetObserved method, which marks the exception as «observed» and prevents exception escalation towards a termination (crash).
void
TaskScheduler_UnobservedTaskException(
object
sender, UnobservedTaskExceptionEventArgs e)
{
ProcessError(e.Exception);
e.SetObserved();
}
The catch-all Unhandled Handler!
Putting those three handlers together, we get the following:
namespace
MyProject
{
public
partial
classApp : Application
{
public
App()
{
DispatcherUnhandledException += App_DispatcherUnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Startup +=
new
StartupEventHandler(App_Startup);
}
void
App_Startup(
object
sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
In the examples below, I show how to try and mark the exceptions as handled/observed, and continue execution. However, you may just want to close «gracefully», with a polite message, instead of an all-out crash.
One thing is certain, you will probably want a unified way of handling the exceptions, wherever they come from, as shown below.
Drilling down to include the Inner Exceptions
As mentioned before, the real reason for an error could be buried several layers down in Inner Exceptions. The three handlers above shared one error logging method, shown below, which iterates down till there are no more Inner Exceptions.
private
void
ProcessError(Exception exception)
{
var error =
"Exception = "
+ exception.Message;
while
(exception.InnerException !=
null
)
{
exception = exception.InnerException;
error +=
" : Inner Exception = "
+ exception.Message;
}
//This is where you save to file.
MessageBox.Show(error);
}
Early attention to error handling in your application will always help production, because most bugs occur during development.
See Logging below.
↑
Return to Top
Commenting Out and Binary Chopping
If a bug in your application is being particularly hard to find, there may be only one way left to find the culprit. It’s very simple, and is often simply the fastest way to drill down to the offender.
The method is simply to remove (or comment out) sections of your code, or XAML, until you get execution PAST the error.
Then you start replacing parts of the removed code, until you find the exact control or method that is causing the problem.
It is referred to as binary chopping because you cut the code into two, to get past the error, then in two again, replacing half of the removed code, and so on until you get down to the offending code/xaml.
You can comment out huge chunks of your code or markup in Visual Studio with ctrl-k + c, then ctrl-k + u to uncomment. Both functions also available in the edit / advanced menu.
This is one of the advantages of writing your application to the MVVM design pattern. MVVM separates your UI (View) from the business logic supplied by the ViewModel, or the Model itself. This means the View is much more loosely coupled, and there are no
hooks from code-behind, acting directly on controls, like referring to controls by name (x:Name) like MyTextBox.Text = «hello». In MVVM the emphasis is all on the bindings, so commenting out half your XAML just means less binding requests, and commenting out
half your code/properties means more binding errors, but not a crash.
Even an MVVM setup can have problems when you try to comment out half the markup. Controls may be relying on other controls, with direct biding in XAML. So there is always a certain amount of «knock-on» effects that also need commenting out, but experience
often shows it is quite simply a very fast and effective way to find the sinner.
Just make sure you keep a copy of the original file (or have recently committed your code to source control) before such dramatic actions. Just in case you make an edit that looses your comment/cut history
↑
Return to Top
Is It Reproducable?
When you hit a brick wall, many people turn to the
MSDN forums, for help and advice from other developers. Often, just the act of trying to put a problem into words for others to understand is equivalent to having a colleague look over your shoulder, which often catches those obvious «too close to the trees»
kind of mistakes that we all make.
However, if the problem isn’t obvious from your description, you will get a much quicker and accurate response from the forum if you supply a
reproduceable example of your problem in a few code snippets.
Quite often, when you try to reproduce the problem in a new project, you will often find the culpret anyway!
↑
Return to Top
Logging
The other age old method of tracing bugs in your application, both in development and once released, is to keep a log of messages from every notable step of your application.
If your bug is of a composite nature (comprising of several different causes), or developing after several iterations, or at an unknown point, you will need to build a picture of what is happening, before you can even know where to start.
Logging Level
Writing comments and session data to a log file means scattering «WriteToLog» type methods throughout your code. It is therefore common to build a «LogLevel» parameter into your methods, allowing you to control the amount of spam that gets written. In a
published application you would only want to capture critical messages, but with a config file flag or startup parameter that allows you to crank up the debug logging (logging ALL messages), for a client that experiences a bug.
Repository Choice
A log repository can be anything from a text file, to a database. One word of caution however. It is no good logging errors to a database if it was a database connection error. So file based logging for critical errors is strongly advised. Then you simply
have a check method on application startup, for any waiting critical error files, that may have been generated when the application crashed. You can then ‘process’ them (into a database, or send by email) and then delete.
It is as explained, quite simple to create your own logging architecture for your application. The key to success is to START EARLY! Don’t try to strap error handling or logging on later, as it will never be as useful as if you had started from the outset.
Also, the majority of bugs are caused during development anyway, so early attention to error handling and logging may take slightly longer to code, but will help speed up the overall production process.
Why Reinvent the Wheel
You could have a lot of fun building your own error logging framework, that is thread safe and foolproof. Or if you’re pushed for time or more interested in higher things, why not just adopt a tried and tested logging framework. Here are some options, but
you should shop around, as new kids often come on the block.
Log4Net
One of the most established frameworks. «log4net is a tool to help the programmer output log statements to a variety of output targets. In case of problems with an application, it is helpful to enable logging so that
the problem can be located. With log4net it is possible to enable logging at runtime without modifying the application binary. The log4net package is designed so that log statements can remain in shipped code without incurring a high performance cost. It follows
that the speed of logging (or rather not logging) is crucial. At the same time, log output can be so voluminous that it quickly becomes overwhelming. One of the distinctive features of log4net is the notion of hierarchical loggers. Using these loggers it is
possible to selectively control which log statements are output at arbitrary granularity. log4net is designed with two distinct goals in mind: speed and flexibility»
ELMAH
A very popular framework. «Stands for Logging Modules and Handlers. An application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications
on a machine, without any need for re-compilation or re-deployment.»
NLog
Newer but 3rd popular «A logging platform for .NET with rich log routing and management capabilities. It can help you produce and manage high-quality logs for your application regardless of its size or complexity.»
NuGet
All of the above frameworks are available through NuGet. If you don’t already have NuGet, it is like an App store for Visual Studio application development. If you want a logging framework, or an MVVM framework, simply open the package manager and search.
Results are listed by popularity. Click to install, and the package gets downloaded and integrated into your current project. That means all the dlls are added and registered, and any supporting files are added to your project. It takes all the pain out of
adding supporting packages to your project. This is a must for all developers. No excuses, get it now!
↑
Return to Top
See Also
- Troubleshooting Portal
- See routed events article
http://code.msdn.microsoft.com/silverlight/WPFSilverlight-Routed-49a16914
↑
Return to Top
Community Resources
- Debugging WPF — WPF Tree Visualizer + Helper Class
- How to: Use the WPF Tree Visualizer
- How to: Display WPF Trace Information
- Trees in WPF
- Breakpoints
- Locals Window
- Threads Window
- Output Window
- Exception.InnerException Property
- Application.DispatcherUnhandledException Event
- AppDomain.UnhandledException Event
- Dispatcher.UnhandledException Event
- Tips on how to debug and learn about WPF
- WPF performance Suite (in Windows SDK)
- UI Spy (in Windows SDK)
- Spy++
- StyleSnooper
- Optimizing WPF Application Performance
- Debugging Tools (.NET Framework)
- Debugging in Visual Studio
- Debugging Managed Code
- Debugging, Tracing, and Profiling
↑
Return to Top
I was also going to recommend Bea Stollnitz’s article but Jonathan Allen got his post in while I was still typing this one. I also recommend the links in this blog entry.
In this particular case you can see that somewhere a ListBoxItem has a FindAncestor binding to an ItemsControl that is failing. That tells you right away there is a ListBoxItem somewhere that is either:
- Not in the visual tree, or
- Not under an ItemsControl (a ListBox is an ItemsControl)
In addition, you know that someone, somewhere, is binding a ListBoxItem’s VerticalContentAlignment property to FindAncestor.
Looking at the system themes (shipped with Expression Blend and also available through NET Reflector’s BAMLViewer Add-in), we see this:
<Style x:Key="{x:Type ListBoxItem}">
<Setter Property="VerticalContentAlignment"
Value="{Binding Path=VerticalContentAlignment,RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}" />
This explains where the binding comes from. The next question is, how is a ListBoxItem being created that is not under a ListBox (or other ItemsControl)?
Some things to look for:
- Are you constructing ListBoxItems in code anywhere?
- Is there any ListBoxItem explicitly specified in your XAML?
- Do you have any code that manually manipulates the items in ListBox?
Hopefully this will head you in the right direction.
Data binding:
Since data bindings are evaluated at runtime, and no exceptions are thrown when they fail, a bad binding can sometimes be very hard to track down. These
problems can occur in several different situations, but a common issue is when you try to bind to a property that doesn’t exist, either because you
remembered its name wrong or because you simply misspelled it. Here’s an example:
<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBindingDebuggingSample" Height="100" Width="200">
<Grid Margin="10" Name="pnlMain">
<TextBlock Text="{Binding NonExistingProperty, ElementName=pnlMain}" />
</Grid>
</Window>
The Output window
The first place you will want to look is the Visual Studio Output window. It should be at the bottom of your Visual Studio window, or you can activate it
by using the [Ctrl+Alt+O] shortcut. There will be loads of output from the debugger, but somewhere you should find a line like this, when running the above
example:
System.Windows.Data Error: 40 : BindingExpression path error: ‘NonExistingProperty’ property not found on ‘object’ »Grid’ (Name=’pnlMain’)’.
BindingExpression:Path=NonExistingProperty; DataItem=’Grid’ (Name=’pnlMain’); target element is ‘TextBlock’ (Name=»); target property is ‘Text’ (type
‘String’)
This might seem a bit overwhelming, mainly because no linebreaks are used in this long message, but the important part is this:
‘NonExistingProperty’ property not found on ‘object’ »Grid’ (Name=’pnlMain’)’.
It tells you that you have tried to use a property called «NonExistingProperty» on an object of the type Grid, with the name pnlMain. That’s actually
pretty concise and should help you correct the name of the property or bind to the real object, if that’s the problem.
Adjusting the trace level
The above example was easy to fix, because it was clear to WPF what we were trying to do and why it didn’t work. Consider this next example though:
<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBindingDebuggingSample" Height="100" Width="200">
<Grid Margin="10">
<TextBlock Text="{Binding Title}" />
</Grid>
</Window>
I’m trying to bind to the property «Title», but on which object? As stated in the article on data contexts, WPF will use the DataContext property on the
TextBlock here, which may be inherited down the control hierarchy, but in this example, I forgot to assign a data context. This basically means that I’m
trying to get a property on a NULL object. WPF will gather that this might be a perfectly valid binding, but that the object just hasn’t been initialized
yet, and therefore it won’t complain about it. If you run this example and look in the Output window, you won’t see any binding errors.
However, for the cases where this is not the behavior that you’re expecting, there is a way to force WPF into telling you about all the binding problems it
runs into. It can be done by setting the TraceLevel on the PresentationTraceSources object, which can be found in the System.Diagnostics namespace:
<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="DataBindingDebuggingSample" Height="100" Width="200">
<Grid Margin="10">
<TextBlock Text="{Binding Title, diag:PresentationTraceSources.TraceLevel=High}" />
</Grid>
</Window>
Notice that I have added a reference to the System.Diagnostics namespace in the top, and then used the property on the binding. WPF will now give you loads
of information about this specific binding in the Output window:
System.Windows.Data Warning: 55 : Created BindingExpression (hash=2902278) for Binding (hash=52760599)
System.Windows.Data Warning: 57 : Path: 'Title'
System.Windows.Data Warning: 59 : BindingExpression (hash=2902278): Default mode resolved to OneWay
System.Windows.Data Warning: 60 : BindingExpression (hash=2902278): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 61 : BindingExpression (hash=2902278): Attach to System.Windows.Controls.TextBlock.Text (hash=18876224)
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 64 : BindingExpression (hash=2902278): Resolve source deferred
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source (last chance)
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 77 : BindingExpression (hash=2902278): Activate with root item <null>
System.Windows.Data Warning: 105 : BindingExpression (hash=2902278): Item at level 0 is null - no accessor
System.Windows.Data Warning: 79 : BindingExpression (hash=2902278): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 87 : BindingExpression (hash=2902278): TransferValue - using fallback/default value ''
System.Windows.Data Warning: 88 : BindingExpression (hash=2902278): TransferValue - using final value ''
By reading through the list, you can actually see the entire process that WPF goes through to try to find a proper value for your TextBlock control.
Several times you will see it being unable to find a proper DataContext, and in the end, it uses the default {DependencyProperty.UnsetValue} which
translates into an empty string.
Using the real debugger
The above trick can be great for diagnosing a bad binding, but for some cases, it’s easier and more pleasant to work with the real debugger. Bindings
doesn’t natively support this, since they are being handled deep inside of WPF, but using a Converter, like shown in a previous article, you can actually
jump into this process and step through it. You don’t really need a Converter that does anything useful, you just need a way into the binding process, and
a dummy converter will get you there:
<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:WpfTutorialSamples.DataBinding"
Title="DataBindingDebuggingSample" Name="wnd" Height="100" Width="200">
<Window.Resources>
<self:DebugDummyConverter x:Key="DebugDummyConverter" />
</Window.Resources>
<Grid Margin="10">
<TextBlock Text="{Binding Title, ElementName=wnd, Converter={StaticResource DebugDummyConverter}}" />
</Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Data;
using System.Diagnostics;
namespace WpfTutorialSamples.DataBinding
{
public partial class DataBindingDebuggingSample : Window
{
public DataBindingDebuggingSample()
{
InitializeComponent();
}
}
public class DebugDummyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debugger.Break();
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debugger.Break();
return value;
}
}
}
In the Code-behind file, we define a DebugDummyConverter. In the Convert() and ConvertBack() methods, we call Debugger.Break(), which has the same effect
as setting a breakpoint in Visual Studio, and then return the value that was given to us untouched.
In the markup, we add a reference to our converter in the window resources and then we use it in our binding. In a real world application, you should
define the converter in a file of its own and then add the reference to it in App.xaml, so that you may use it all over the application without having to
create a new reference to it in each window, but for this example, the above should do just fine.
If you run the example, you will see that the debugger breaks as soon as WPF tries to fetch the value for the title of the window. You can now inspect the
values given to the Convert() method, or even change them before proceeding, using the standard debugging capabilities of Visual Studio.
If the debugger never breaks, it means that the converter is not used. This usually indicates that you have an invalid binding expression, which can be
diagnosed and fixed using the methods described in the start of this article. The dummy-converter trick is only for testing valid binding expressions.
This article has been fully translated into the following languages:
-
Bulgarian
-
Chinese
-
Danish
-
French
-
German
-
Italian
-
Japanese
-
Polish
-
Portuguese
-
Russian
-
Spanish
-
Swedish
-
Vietnamese
Is your preferred language not on the list? Click here to help us translate this article into your language!
- Remove From My Forums
-
Question
-
Hi,
We are using the MVVM pattern in our WPF applications, and I’d like to know if there is some way to make the app crash or receive some kind of runtime error when having binding errors when executing a WPF application.
I know it is possible to check the output window in Visual Studio to read the errors, but I’d like to have a more noticable kind of error if possible.
Thank you very much,
Igor.
Igor
Answers
-
-
Marked as answer by
Wednesday, February 3, 2010 5:04 AM
-
Marked as answer by
-
Hi Igor,
As far as I know, you can set ValidatesOnExceptions of the Binding object to true, and when there are exceptions occured, it will be added to Validation.Errors. This way does not force the app to crash, but may indicate you that exception is thrown by binding. Here is an article might help you understand—-Handling and Reporting WPF Data Binding Validation Errors and Exceptions
If your purpose is only to help assist debugging the binding errors in the application, there are typically ways other than checking out the output window of Visual Studio. You can check out this article for more information.
Best regards,
Bruce Zhou
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.-
Marked as answer by
Bruce.Zhou
Friday, December 4, 2009 1:39 AM
-
Marked as answer by
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
1
branch
2
tags
Code
-
Use Git or checkout with SVN using the web URL.
-
Open with GitHub Desktop
-
Download ZIP
Latest commit
Files
Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
Turn WPF binding errors into exceptions
A lightweight library that converts WPF binding errors into exceptions, so you can quickly spot errors in the XAML markup and detect them unit tests.
All it requires is a single line in your existing code:
public partial class App : Application { protected override void OnStartup(StartupEventArgs args) { base.OnStartup(args); // Start listening for WPF binding error. // After that line, a BindingException will be thrown each time // a binding error occurs. BindingExceptionThrower.Attach(); } }
Installing:
With .NET CLI
> dotnet add package WpfBindingErrors
With Package Manager:
PM> Install-Package WpfBindingErrors
https://www.nuget.org/packages/WpfBindingErrors
Content
- Project WpfBindingError is a reusable assembly that listens for binding errors.
- Project SampleWpfApplication shows how to throw
BindingException
at runtime. - Project SampleWpfApplicationTests shows how to check binding errors in a unit test project.
Each project contains a dedicated README with more detail.
Supported frameworks
- .NET Core 3.0 🆕
- .NET Framework 4.6
- .NET Framework 4.5.2
- .NET Framework 4.5.1
- .NET Framework 4.5
- .NET Framework 4.0
Contributors
- Gareth Brown
- Bruno Juchli
Data binding establishes a connection between the application UI and business logic. When it works, it’s a wonderful thing. You no longer have to write code that updates your UI or pass values down to your business logic. When it breaks, it can be frustrating to figure out what went wrong. In this post, I will give you some tips on how you can debug your data bindings in WPF.
1. Add Tracing to the Output Window
Here is a sample TextBlock
that has a missing data context. In this situation, you will not get any errors in the Visual Studio output window.
To enable tracing, I added a new xml namespace to include the System.Diagnostics
namespace. You can also set the level of tracing to High
, Medium
, Low
, or None
. Now let’s add some tracing to the output window to see what is wrong with the data binding.
Now the output window will contain the following helpful information:
System.Windows.Data Warning: 71 : BindingExpression (hash=38600745): DataContext is null
2. Attach a Value Converter to Break into the Debugger
When you don’t see anything displayed in your UI, it is hard to tell whether it’s data binding causing your issue or a problem with the visual layout of the control. You can eliminate the data binding as the problem by adding a value converter and break into the debugger. If the value is what you expected, then data binding is not your issue.
Here is a simple value converter that breaks into the debugger.
using System; using System.Diagnostics; using System.Globalization; using System.Windows.Data; namespace WpfApplication1 { public class DebugDataBindingConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Debugger.Break(); return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { Debugger.Break(); return value; } } }
To use the value converter, reference the namespace of the assembly that contains the converter and add an instance of it to the resources of your window. Now add the converter to your problematic data binding.
3. Know the Instant You Have a Data Binding Problem
Unless you are constantly checking every UI element and monitoring the output window for binding errors, you will not always catch that you have a data binding problem. An exception is not thrown when data binding breaks, so global exception handlers are of no use.
Wouldn’t it be nice if you break into the debugger the instant you have a data binding error? By adding our own implementation of a TraceListener
that breaks into the debugger, we will get notified the next time we get a data binding error. I also added the default ConsoleTraceListener
alongside our new DebugTraceListener
, so that our previous examples of tracing output would not be broken.
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { PresentationTraceSources.Refresh(); PresentationTraceSources.DataBindingSource.Listeners.Add(new ConsoleTraceListener()); PresentationTraceSources.DataBindingSource.Listeners.Add(new DebugTraceListener()); PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Warning | SourceLevels.Error; base.OnStartup(e); } } public class DebugTraceListener : TraceListener { public override void Write(string message) { } public override void WriteLine(string message) { Debugger.Break(); } }
With these tips, you should have a more pleasant data binding experience. Please share any additional debugging tips you may have in the comments.
By default, Visual Studio (v2012 onwards) is configured to automatically log any binding errors detected whilst running a WPF application.
You can see examples of such in the Debug Output window:
System.Windows.Data Error: 40 : BindingExpression path error: ‘MyUnknownProperty’ property not found on ‘object’ »Product’ (HashCode=30581329)’. BindingExpression:Path=MyUnknownProperty; DataItem=’Product’ (HashCode=30581329); target element is ‘TextBox’ (Name=»); target property is ‘Text’ (type ‘String’)
This setting can be adjusted using the Debugging, Output Window settings:
Often, it’s useful to be notified in a more intrusive manner when a binding error has been detected…rather than silently continuing, just as WPF does.
In order to add your own handling, you need to add a class that derives from TraceListener to the PresentationTraceSources.DataBindingSource.Listeners collection.
Your TraceListener needs to implement the following abstract methods:
public override void WriteLine(string message)
public override void Write(string message)
Generally, you’ll only add implementation details to the WriteLine method. When a binding error is detected the WPF will call your WriteLine and Write methods.
Full Source:
Listing
internal class BindingListener : TraceListener{
public static readonly BindingListener Current = new BindingListener(); private const int Uninitialised = 0; private const int Initialised = 1; private int _initialised = Uninitialised; public void Initialise() { if (Interlocked.CompareExchange(ref _initialised, Initialised, Uninitialised) == Uninitialised) { PresentationTraceSources.DataBindingSource.Listeners.Add(this); } } public override void WriteLine(string message) { throw new ApplicationException(message); } public override void Write(string message) {} }In the above example we wrap access to the BindingListener with a static Current property. When Initialise() is called, provided it’s the first time is has been called, then the class is added to PresentationTraceSources.DataBindingSource.Listeners
It’s probably overkill using Interlocked.CompareExchange for thread-safety as generally you’ll be calling Initialise() from the main GUI thread, as your app starts up:
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { BindingListener.Current.Initialise(); base.OnStartup(e); } }When you run the application (in debug mode) and there is any form of binding error, you'll see following exception dialog:
This can be quite annoying, but at least you won't miss any binding errors.
One thing to note: your listener only gets called in Debug builds!
Debug Databinding Issues in WPF
DataBinding is one of the most powerful features in WPF. But because it resolves the bindings at runtime and does not throw exceptions, it’s sometimes hard to find the reason why the data do not appear as expected.
There are mainly two reasons:
- The DataBinding expression is invalid. Then use Trace Output to
resolve. - The DataBinding expression is valid, but the result is not the expected. Then use a Debug Converter to resolve it.
Method 1: Trace messages in the output window
In the example, the text property of the TextBlock is bound to the property «InvalidPath» of the StackPanel — which does not exists.
<Window x:Class="DebugDataBinding.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <StackPanel x:Name="stack"> <TextBlock Text="{Binding ElementName=stack, Path=InvalidPath}" /> </StackPanel> </Window>
In this case the invalid databinding expression is reported by a trace message in the output window
System.Windows.Data Error: 39 : BindingExpression path error: 'InvalidPath' property not found on 'object' ''StackPanel' (Name='stack')'. BindingExpression:Path=InvalidPath; DataItem='StackPanel' (Name='stack'); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
Note: Binding to a path of a property that has NULL value is a valid expression and does not generate an error message (for e.g. binding to a property of the data context that is NULL).
Adjust the trace level (.NET 3.5 and higher)
NET3.5 has a new feature that allows you to set the level of details of trace messages to None
, Low
, Medium
or High
.
To set the trace level you have to include an extra namespace to your XAML:
<Window x:Class="DebugDataBinding.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"> <StackPanel x:Name="stack"> <TextBlock Text="{Binding ElementName=stack, Path=InvalidPath, diag:PresentationTraceSources.TraceLevel=High}" /> </StackPanel> </Window>
The following snipped shows how to adjust the trace level by code:
PresentationTraceSources.DataBindingSource.Listeners.Add( new ConsoleTraceListener()); PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.All;
Method 2: Use a ValueConverter to break into the debugger
A simple trick is to write a value converter that does nothins except breaking into the debugger. All you need to do now is to add this converter to the binding expression that fails and you can easily see the values that should be bound.
/// <summary> /// This converter does nothing except breaking the /// debugger into the convert method /// </summary> public class DatabindingDebugConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Debugger.Break(); return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { Debugger.Break(); return value; } }
To use the converter in XAML, reference the namespace of the assembly that contains the converter and add an instance of it to the resources of your window.
<Window x:Class="DebugDataBinding.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DebugDataBinding" Title="Window1" Height="300" Width="300"> <Window.Resources> <local:DatabindingDebugConverter x:Key="debugConverter" /> </Window.Resources> <StackPanel x:Name="stack"> <TextBlock Text="{Binding ElementName=stack, Path=ActualWidth, Converter={StaticResource debugConverter}}" /> </StackPanel> </Window>
Last modified: 2009-06-08 08:31:10
Copyright (c) by Christian Moser, 2011.
Comments on this article
Show all comments
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|