Table of Contents
Introduction
Binding Errors
This is a ComboBox inside a DataGridTemplateColumn. I am binding its ItemsSource to a property called PartIds.
"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')"
1. Changing Trace Level for All in Options
(click to enlarge)
2. Changing Trace Level on a Specific Binding
<
ComboBox
ItemsSource
=
"{Binding PartIdz,
PresentationTraceSources.TraceLevel=High, RelativeSource={RelativeSource AncestorType=Window}}"
. . . />
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
(click to enlarge)
Other inspection tools
Debuging Your Code
Inner Exceptions
Unhandled Exceptions
You can download and test all five handlers here : Handling Unhandled Exceptions in WPF (The most complete collection of handlers)
Application.DispatcherUnhandledException
<
Application
x:Class
=
"MyProject.App"
StartupUri
=
"MainWindow.xaml"
DispatcherUnhandledException
=
"App_DispatcherUnhandledException"
>
<
Application.Resources
/>
</
Application
>
namespace
MyProject
{
public
partial
class
App : Application
{
public
App()
{
DispatcherUnhandledException +=
new
System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(App_DispatcherUnhandledException);
}
void
App_DispatcherUnhandledException(
object
sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
ProcessError(e.Exception);
e.Handled =
true
;
}
AppDomain.CurrentDomain.UnhandledException
<
Application
x:Class
=
"UnhandledExceptionHandler.App"
StartupUri
=
"MainWindow.xaml"
Startup
=
"App_Startup"
>
<
Application.Resources
/>
</
Application
>
namespace
MyProject
{
public
partial
class
App : 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!"
);
}
TaskScheduler.UnobservedTaskException
namespace
MyProject
{
public
partial
class
App : Application
{
public
App()
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
void
TaskScheduler_UnobservedTaskException(
object
sender, UnobservedTaskExceptionEventArgs e)
{
ProcessError(e.Exception);
e.SetObserved();
}
The catch-all Unhandled Handler!
namespace
MyProject
{
public
partial
class
App : 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;
}
Drilling down to include the 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.
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
reproducable 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!
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 prodiuction 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 naow!!
↑ Return to Top
See Also
- Troubleshooting Portal
- See routed events article http://code.msdn.microsoft.com/silverlight/WPFSilverlight-Routed-49a16914
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