Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

Debugging WPF data bindings

Rate me:
Please Sign up or sign in to vote.
4.91/5 (19 votes)
29 Aug 2011CPOL7 min read 78.2K   43   14
Explains how to debug WPF data bindings using trace messages or an IValueConverter.

Introduction

Data bindings is one of the most widely used features of WPF. However, debugging data bindings is not as well known, which I'll attempt to rectify with this article.

There are essentially two methods we can use to debug our data bindings, when they do not throw an exception.

  1. Using IValueConverter to step into the debugger.
  2. Using the trace messages.

Using IValueConverter

This method is pretty straightforward. You'll create a class implementing the IValueConverter interface and then use this converter in your binding so you can step into the debugger.

C#
// Either you can use Debugger.Break() or put a break point
// in your converter, inorder to step into the debugger.
// Personally I like Debugger.Break() as I often use
// the "Delete All Breakpoints" function in Visual Studio.
public class MyDebugConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
           object parameter, CultureInfo culture)
    {
        System.Diagnostics.Debugger.Break();
        return value;
    }

    public object ConvertBack(object value, Type targetType, 
           object parameter, CultureInfo culture)
    {
        System.Diagnostics.Debugger.Break();
        return value;
    }
}
XML
<TextBox Text="{Binding MyProperty, Converter={StaticResource MyDebugConverter}}"/>

Now whenever your binding provides a new value, the debugger will break at Debugger.Break() in your converter and you can step out to see which property is set and where it was set.

Sample Image

When to use the IValueConverter method

The usefulness of this method is rather limited. It really does nothing but allows you to locate the piece of code that changes your property. So you should use it when your binding is working, but you're not getting the expected value. If your binding source is using INotifyPropertyChanged, then you could just put a breakpoint in the setter of your property in order to avoid using the converter.

A quick tip, if you're going to use this method, you might want to read this article by marlongrech, he demonstrates how you can use the MarkupExtension so you don't have to make a resource entry for your converter.

Notice

If you use the Debugger.Break() and forget to remove the converter from your binding, you won't like the result when you make a release.

Image 2

This is what happens when you run your application without a debugger attached to it. According to the documentation, it should ask the user if they want to attach a debugger, but that doesn't happen on my end.

Using trace messages

Details about a binding error is shown in your Output window whenever a binding fails to locate the binding source or a property on the source. You can use the key shortcut Ctrl+Alt+O to activate your output window. The errors from data bindings will start with "System.Windows.Data Error: ##".

Visual Studio output window, the highlighted line is the description of a data binding error

Binding errors

'<MyProperty>' property not found on 'object' ''<MySource>'

Reasons:

  1. The name of <MyProperty> is misspelled.
  2. The property has been removed from the <MySource> type or not added yet.
  3. The <MySource> type is not expected. E.g., it's MyViewModel but we expected it to be MyOtherViewModel.

Solutions:

  1. The correct name of <MyProperty> (copy-pasting the property name will ensure 100% accuracy; remember, the real property name might be misspelled as well).
  2. If removed, then the view might need to be reworked, so find out why the property has been removed before you go re-implementing the property (if it's even possible). If it hasn't been added, be patient, your coworkers probably got as much on their plate as you.
  3. How we go about solving this one is dependent on how the source is determined.
    1. The source is the DataContext. Handle the DataContextChanged event of the target element in order to step into the debugger and find out where and why the DataContext is not set to the expected value (just like when using IValueConverter).
    2. RelativeSource is used; see: Cannot find source for binding with reference - solution 1.
    3. The binding's Source property has been set; go to the XAML or code that sets this property and correct it.

Cannot find source for binding with reference

Reasons:

  1. The ElementName property of the binding is set, but no element with that name could be found.
  2. The RelativeSource property of the binding is set and the FindAncestor mode is used, but no source was found which satisfied the RelativeSource.

Solutions:

  1. Ensure we've not misspelled the name (do a copy-paste to be 100%sure); if that doesn't work, then the element with the given name is not in the appropriate namescope, see WPF XAML Namescopes.
  2. The first thing we should do is to verify that the RelativeSource is setup as we expected. That means make sure AncestorType is the correct type and if AncestorLevel is used, make sure the level is correct, keep in mind it's 1-based. If the binding still doesn't find the binding source, then we can use PresentationTraceSources.TraceLevel in order to increase the information we get from the binding engine. Notice however that PresentationTraceSources.TraceLevel was first introduced in .NET 3.5 so if you're using .NET 3.0, you'll have to change your target framework for debugging purposes.
  3. Let's have a look at the trace PresentationTraceSources.TraceLevel=High produces:

    C#
    public class MyViewModel
    {
        public string CurrentUserName {get; set}
        public IEnumerable<string /> UserActions { get { ... } }
    }
    XML
    <Window ...>
        <Window.DataContext>
            <MyViewModel>
        </Window.DataContext>
            <ListView ItemsSource="{Binding UserActions}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding DataContext.CurrentUserName,
                          RelativeSource={RelativeSource FindAncestor, 
                                          AncestorType={x:Type Button}},
                          PresentationTraceSources.TraceLevel=High}"/>
                        <TextBlock Text="{Binding}"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Window>
    System.Windows.Data Warning: 63 : BindingExpression (hash=49385318): 
           Resolving source  (last chance)
    System.Windows.Data Warning: 66 : BindingExpression (hash=49385318): 
           Found data context element: <null /> (OK)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried StackPanel (hash=17059405)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried ContentPresenter (hash=29475730)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried Border (hash=51339472)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried ListViewItem (hash=28574894)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried VirtualizingStackPanel (hash=25181126)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried ItemsPresenter (hash=59408853)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried ScrollContentPresenter (hash=56152722)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried Grid (hash=43844556)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried ScrollViewer (hash=26847985)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried Border (hash=199777)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried ListView (hash=8990007)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried ContentPresenter (hash=1897140)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried AdornerDecorator (hash=18262443)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried Border (hash=16503569)
    System.Windows.Data Warning: 69 :     Lookup ancestor of type Button:  
           queried MainWindow (hash=4463106)
    System.Windows.Data Error: 4 : Cannot find source for binding 
           with reference 'RelativeSource FindAncestor, 
           AncestorType='System.Windows.Controls.Button', AncestorLevel='1''. 
           BindingExpression:Path=DataContext.CurrentUserName; DataItem=null; 
           target element is 'TextBlock' (Name=''); 
           target property is 'Text' (type 'String')

    Notice the binding engine doesn't give up easely so we'll see the warnings repeated a few times. Just find the last "BindingExpression (hash=##...): Resolving source"-warning that precedes the error message. When we've done that, we will have an overview of the path the binding engine takes to in order to find the source. It's now up to us to follow that path in our XAML/code to find the point at which we would have expected the Button (in this case) to be and then fix the binding accordingly.

My binding doesn't work, but I do not get any error message

Reasons:

  1. Lack of or Incorrect use of the UpdateSourceTrigger property of the binding.
  2. The binding is using the DataContext as its binding source and the DataContext is null.
  3. One of the properties specified in the Path is null, e.g., User is null in the path "User.FirstName".

Solutions:

  1. Make sure UpdateSourceTrigger is set to the expected value. If the bindings UpdateSourceTrigger property isn't set, then it defaults to the value specified by the target DependencyProperty, e.g., TextBox.Text default UpdateSourceTrigger-value is LostFocus which means the source is only updated when the TextBox loses focus. If the we want the binding to update whenever the target property changes, we'll set UpdateSourceTrigger to PropertyChanged.
  2. If the DataContext or the DataContext of an ancestor element is set through a binding then debug that binding.
  3. Handle the DataContextChanged event of the target element in order to step into the debugger and find out where and why the DataContext is set to null. If there is no code setting the DataContext to null and thus the DataContextChanged event isn't fired, then we'll have to debug the code that is supposed to set the DataContext of the target element or an ancestor element.

In this case, you would want to use the IValueConverter method.

Monitoring existing bindings for errors

When we fix a bug, implement new features, or optimize code, we risk introducing new bugs like breaking a binding. Looking through the output window for binding errors can be annoying as we'll have to filter out all the unrelated information ourselves. In order to filter out all the related information, we can use a listener and a switch to log the trace messages to a file which we can then look through for binding errors. We log the data binding trace messages by adding the following to the configuration section of our app.config.

XML
<system.diagnostics>
    <sources>
        <source name="System.Windows.Data" switchName="mySwitch">
            <listeners>
                <add name="myListener" />
            </listeners>
        </source>
    </sources>

    <switches>
        <add name="mySwitch" value="All" />
    </switches>

    <sharedListeners>
        <add name="myListener" type="System.Diagnostics.TextWriterTraceListener"
             initializeData="DataBindingTrace.txt" />
    </sharedListeners>
</system.diagnostics>

Here we've set the switch to All; this will give us as much information as possible about the data binding. Setting it to Error will result in only the data binding errors being logged. To turn the logging off completely, set it to Off.

Let's look at an example. The following binding fails, because the DataContext is null.

XML
<TextBox DataContext="{x:Null}" Text="{Binding MyProperty}"/>

This produces the following output in our log file:

System.Windows.Data Information: 40 : BindingExpression path error: 'MyProperty' 
  property not found for 'object' because data item is null. This could happen 
  because the data provider has not produced any data yet. BindingExpression:Path=MyProperty; 
  DataItem=null; target element is 'TextBox' (Name=''); 
  target property is 'Text' (type 'String')
System.Windows.Data Information: 19 : BindingExpression 
  cannot retrieve value due to missing information. 
  BindingExpression:Path=MyProperty; DataItem=null; target element 
  is 'TextBox' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Information: 20 : BindingExpression cannot 
  retrieve value from null data item. This could happen when binding 
  is detached or when binding to a Nullable type that has no value. 
  BindingExpression:Path=MyProperty; DataItem=null; target element 
  is 'TextBox' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Information: 10 : Cannot retrieve value 
  using the binding and no valid fallback value exists; 
  using default instead. BindingExpression:Path=MyProperty; 
  DataItem=null; target element is 'TextBox' (Name=''); target property 
  is 'Text' (type 'String')

From the this, we can see at the first line that the binding source is null and thus the source of our error, which we debug as described in Solution 1 of My binding doesn't work, but I do not get any error message.

Conclusion

I hope you've found this article informative. My goal was to communicate how and where we can find information about data binding errors as well as how we can interpret them.

Any feedback you may have is very welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Nexcom
Denmark Denmark
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSuggestion to Debugger.Break() Pin
Silve_X8-Mar-12 9:45
Silve_X8-Mar-12 9:45 
GeneralMy vote of 5 Pin
Ashley Davis6-Sep-11 21:04
Ashley Davis6-Sep-11 21:04 
GeneralRe: My vote of 5 Pin
Simon Bang Terkildsen7-Sep-11 4:02
Simon Bang Terkildsen7-Sep-11 4:02 
QuestionVery nice article Simon Pin
Espen Harlinn30-Aug-11 11:48
professionalEspen Harlinn30-Aug-11 11:48 
AnswerRe: Very nice article Simon Pin
Simon Bang Terkildsen30-Aug-11 12:35
Simon Bang Terkildsen30-Aug-11 12:35 
GeneralMy vote of 5 Pin
JamesHurst29-Aug-11 20:49
JamesHurst29-Aug-11 20:49 
GeneralRe: My vote of 5 Pin
Simon Bang Terkildsen29-Aug-11 22:35
Simon Bang Terkildsen29-Aug-11 22:35 
QuestionVery helpful Pin
BillW3329-Aug-11 9:03
professionalBillW3329-Aug-11 9:03 
AnswerRe: Very helpful Pin
Simon Bang Terkildsen29-Aug-11 9:26
Simon Bang Terkildsen29-Aug-11 9:26 
GeneralMy vote of 5 Pin
mbcrump23-Aug-11 7:40
mentormbcrump23-Aug-11 7:40 
GeneralRe: My vote of 5 Pin
Simon Bang Terkildsen23-Aug-11 8:49
Simon Bang Terkildsen23-Aug-11 8:49 
QuestionGood job Pin
Sacha Barber23-Aug-11 2:27
Sacha Barber23-Aug-11 2:27 
Bea did this a while back, most people would have been lost without her blog back then : http://bea.stollnitz.com/blog/?p=52
Sacha Barber
  • Microsoft Visual C# MVP 2008-2011
  • Codeproject MVP 2008-2011
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

AnswerRe: Good job Pin
Simon Bang Terkildsen23-Aug-11 3:01
Simon Bang Terkildsen23-Aug-11 3:01 
GeneralRe: Good job Pin
Sacha Barber23-Aug-11 3:22
Sacha Barber23-Aug-11 3:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.