How to never miss a WPF binding error again

There are a lot of things that can go wrong with a Binding in WPF. Maybe the DataContext is not set as expected, the binding does not convert from source to target type as expected or the property you are binding is not initialized yet. A typical error looks like this:

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=IDontExist; DataItem=null; target element is ‘TextBlock’ (Name=”); target property is ‘Text’ (type ‘String’)

Often times these binding errors do not cause our program to crash. But they can cause wrong or missing displayed values on the UI. Either way they always hint to some binding issues and should  be resolved. The Visual Studio debugger outputs binding errors to the output window by default. But it does not break. Because of this, binding errors often get overseen.

But there is an easy way arround that. We can add a custom TraceListener to the DataBinding-TraceSource and set the trace level in code. The following code snipped shows a class that calls a delegate when the custom TraceListener gets invoked by the framework.

using System;
using System.Diagnostics;

namespace RS.WPF.Framework.Util
{
    public class BindingErrorListener : TraceListener
    {
        private readonly Action<string> _errorHandler;

        public BindingErrorListener(Action<string> errorHandler)
        {
            _errorHandler = errorHandler;
            TraceSource bindingTrace = PresentationTraceSources
                .DataBindingSource;

            bindingTrace.Listeners.Add(this);
            bindingTrace.Switch.Level = SourceLevels.Error;
        }

        public override void WriteLine(string message)
        {
            _errorHandler?.Invoke(message);
        }

        public override void Write(string message)
        {
        }
    }
}

We can use the SourceLevels-enum to configure what kind of binding issues are traced. We can use this code for example in the app constructor to register a binding error listener and break the debugger when a binding error occurs.

public partial class App : Application
{
    public App()
    {
        new BindingErrorListener(msg => Debugger.Break());
    }
}

This way we wont miss a binding issue again while debugging our application.

Leave a Reply

Your email address will not be published. Required fields are marked *