#1,209 – Catching Data Binding Errors Part 2

In Part 1, I described how to use a TraceSource to intercept data binding errors. Below, we convert these binding errors into exceptions. You then can know that the error will get reported at runtime and you can catch and handle the exception as you like.

(Code below is inspired by WpfBindingErrors project and Jason Bock’s blog post).

Building on the code in the earlier post, we modify the WriteLine method to parse the resulting error message and build up a custom exception type.

    public class BindingErrorException : Exception
    {
        public string SourceObject { get; set; }
        public string SourceProperty { get; set; }
        public string TargetElement { get; set; }
        public string TargetProperty { get; set; }

        public BindingErrorException() 
            : base() { }

        public BindingErrorException(string message)
            : base(message) { }

    }

    public class BindingErrorTraceListener : TraceListener
    {
        private const string BindingErrorPattern = @"^BindingExpression path error(?:.+)'(.+)' property not found(?:.+)object[\s']+(.+?)'(?:.+)target element is '(.+?)'(?:.+)target property is '(.+?)'(?:.+)$";

        public override void Write(string s) { }

        public override void WriteLine(string message)
        {
            var xx = new BindingErrorException(message);

            var match = Regex.Match(message, BindingErrorPattern);
            if (match.Success)
            {
                xx.SourceObject = match.Groups[2].ToString();
                xx.SourceProperty = match.Groups[1].ToString();
                xx.TargetElement = match.Groups[3].ToString();
                xx.TargetProperty = match.Groups[4].ToString();
            }

            throw xx;
        }
    }

As before, we attach a listener at startup. We also catch unhandled binding exceptions and display a MessageBox.

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            PresentationTraceSources.Refresh();
            PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error;
            PresentationTraceSources.DataBindingSource.Listeners.Add(new BindingErrorTraceListener());

            DispatcherUnhandledException += App_DispatcherUnhandledException;
        }

        private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            var bex = e.Exception as BindingErrorException;
            if (bex != null)
            {
                MessageBox.Show($"Binding error. {bex.SourceObject}.{bex.SourceProperty} => {bex.TargetElement}.{bex.TargetProperty}");
            }
        }
    }

For a bad property name that leads to a binding error, we then get a nice little error.

 

#1,208 – Catching Data Binding Errors Part 1

In WPF, data binding errors by default are quietly swallowed, rather than throwing an exception.

Happily, there is a TraceSource that we can use to intercept all binding errors. All data binding errors will go through this trace source and we can therefore add a TraceListener to the trace source’s Listeners collection in order to intercept the error.

We start by creating a simple class deriving from TraceListener that overrides Write and WriteLine methods. These methods will be invoked when messages come from the trace source. Note that we don’t yet do anything useful with these messages, but just dump them to standard trace output (to prove to ourselves that we got them).

   public class BindingErrorTraceListener : TraceListener
    {
        public override void Write(string message)
        {
            Trace.WriteLine(string.Format("==[Write]{0}==", message));
        }

        public override void WriteLine(string message)
        {
            Trace.WriteLine(string.Format("==[WriteLine]{0}==", message));
        }
    }
}

We can now wire our listener into the standard data binding trace source. Below, we do this at application startup.


    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error;
            PresentationTraceSources.DataBindingSource.Listeners.Add(new BindingErrorTraceListener());
        }
    }

At this point, we’re not doing anything with the binding errors. But if you watch the output window, you’ll see that we’re now capturing them.

Next time, we’ll look at converting these errors into exceptions that you can catch.

#1,207 – Data Binding Can Fail Quietly in Production Code

By default, if there’s a problem in how you set up data binding in WPF, the user of the application will never see the binding errors. An exception occurs internally for the binding error, but the WPF application won’t crash or report the exception.

Let’s say that we have that we have an application the includes the following properties in an object that we bind to (i.e. the object that we set the DataContext of a window to). (Using SetProp from post #1,205).

        private string _yourText;
        public string YourText
        {
            get { return _yourText; }
            set
            {
                if (SetProp(ref _yourText, value))
                {
                    RaisePropertyChanged("TextLength");
                }
            }
        }

        private int _textLength;
        public int TextLength
        {
            get { return string.IsNullOrWhiteSpace(_yourText) ? 0 : _yourText.Length; }
        }

Now let’s say that we have the following XAML fragment (assume in a Window whose DataContext is set to a class containing the properties shown above).

        <TextBlock Text="Enter text:"/>
        <TextBox Grid.Column="1" Margin="10,0" Text="{Binding YourText, UpdateSourceTrigger=PropertyChanged}"/>

        <TextBlock Grid.Row="1" Margin="0,10" Text="Length:"/>
        <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,10" Text="{Binding TextLenth}"/>

Note the error–we spelled the TextLength property incorrectly, so binding for this TextBlock will fail.

If we just build and run the application, we won’t get an exception and the application will run fine. It just won’t bind to TextLength.

If you run the application in the Visual Studio debugger and pay attention to the Output window, you’ll see the error reported there. But WPF swallows this error and the application runs fine.

#1,205 – Use CallerMemberName Attribute to Make INotifyPropertyChanged Implementation Cleaner

The INotifyPropertyChanged interface is central to using data binding in WPF. You typically create ViewModels containing properties that fire a PropertyChanged event whenever a property value changes. Implementing this plumbing over and over for every property can become tedious. This argues for a reusable pattern to make the per-property code cleaner.

Specifying string-based property names when raising the PropertyChanged event can also be error-prone. When, passing property names as string literals, a misspelled property name doesn’t lead to a compiler warning, but just a quiet data binding failure.

Below is a pattern you can use to make property change code cleaner. It relies on the CallerMemberName attribute, which can be used to default a method parameter to the name of a caller–the property name in this case.

We have a generic method that compares a new property value to the current value in the backing variable. If the value has changed, it assigns the new value and fires the property changed event.

        protected bool SetProp<T>(ref T backingField, T value, [CallerMemberName] string propName = null)
        {
            bool valueChanged = false;

            // Can't use equality operator on generic types
            if (!EqualityComparer<T>.Default.Equals(backingField, value))
            {
                backingField = value;
                RaisePropertyChanged(propName);
                valueChanged = true;
            }

            return valueChanged;
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected void RaisePropertyChanged(string propName)
        {
            if (!string.IsNullOrWhiteSpace(propName) && (PropertyChanged != null))
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

A standard property implementation can now look pretty clean. (Note: We’re not checking the return value of SetProp, but we could do that if we wanted to perform other logic when the property value changes).

        private Dog _selectedDog;
        public Dog SelectedDog
        {
            get { return _selectedDog; }
            set { SetProp(ref _selectedDog, value); }
        }

You would generally put this sort of code in a common base class that all of your ViewModel classes could inherit from.

#1,140 – Using a Value Converter in a Template

You can use a value converter anywhere in XAML where you are using data binding.

Below is an example of using a value converter within a data template.  The Visibility property is bound to the underlying Actor object that is the data context for the item template.  The value converter then derives a value for Visibility from several properties within the Actor object.  (Assume that we have an ActorList property that is a collection of Actor instances).

The XAML includes:

    <Window.Resources>
        <loc:DeadFredConverter x:Key="deadFredConverter"/>
    </Window.Resources>

    <StackPanel>
        <ListBox Margin="15" Width="270" Height="320"
             ItemsSource="{Binding ActorList}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Image}" Height="80"/>
                        <StackPanel Margin="5">
                            <TextBlock Text="{Binding FullName}" FontSize="12" FontWeight="Bold"/>
                            <TextBlock Text="{Binding Dates}"/>
                            <TextBlock Text="{Binding KnownFor}" Margin="0,5,0,0" FontStyle="Italic"/>
                        </StackPanel>
                        <Label Content="Dead Fred !" Foreground="Red"
                               FontWeight="Bold"
                               Visibility="{Binding Converter={StaticResource deadFredConverter}}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>

The body of the value converter is:

    class DeadFredConverter : IValueConverter
    {
        // Convert to Visibility, deriving from properties on Actor object
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Actor a = (Actor)value;

            Visibility vis = Visibility.Hidden;

            if ((a.FirstName == "Fred") &&
                a.DeathYear.HasValue &&
                (a.DeathYear <= DateTime.Today.Year))
                vis = Visibility.Visible;

            return vis;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

1140-001

#1,139 – Value Converter Example for Input

The ConvertBack method of a value converter is used to convert from a binding target (e.g. the attribute of a control) to a binding source (e.g. a property).  Below is a simple example, showing how we can convert from a Slider value to the square root of the selected value.

In XAML, we have a Slider that ranges from 1-100 and binds to a property that is meant to store the square root of the selected value.  We specify a binding Mode to indicate that binding should only map from the target (the Value property) to the source (the SqRootValue property) and not in the other direction.  We then include labels that bind to the Slider’s Value property as well as the SqRootValue property.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Value Converter" SizeToContent="WidthAndHeight">

    <Window.Resources>
        <local:IntToRootConverter x:Key="intToRootConverter"/>
    </Window.Resources>

    <StackPanel Margin="15">
        <Slider x:Name="slider" Minimum="1" Maximum="100"
                IsSnapToTickEnabled="True"
                Value="{Binding Path=SqRootValue,
                                Converter={StaticResource intToRootConverter},
                                Mode=OneWayToSource}"/>
        <Label Content="{Binding ElementName=slider, Path=Value}"/>
        <Label Content="{Binding SqRootValue}"/>
    </StackPanel>
</Window>

The code-behind is straightforward.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            this.DataContext = this;
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void OnPropertyChanged(string prop)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        private double sqRootValue;
        public double SqRootValue
        {
            get { return sqRootValue; }
            set
            {
                if (sqRootValue != value)
                {
                    sqRootValue = value;
                    OnPropertyChanged("SqRootValue");
                }
            }
        }
    }

In the value converter, we just take the square root of the current value, calculating the result in ConvertBack.

    public class IntToRootConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        // Convert from int (target of binding) to double representing square root (source of binding)
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double root = Math.Sqrt((double)value);
            return root;
        }
    }

At run-time:

1139-001

#1,138 – Value Converter Example for Output

The Convert method of a value converter is used to convert from a binding source (e.g. a property) to a binding target (e.g. the attribute of a control).  Below is a simple example, showing how we can convert from an integer to a brush of a particular color.

In XAML, we have a Slider that ranges from 0-255 and binds to a property, storing the integer value that the user selects.  We then bind to the same property for a rectangle shape’s Fill property.  The Fill property wants a Brush object, so we use a value converter to convert from the integer to a brush.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Value Converter" SizeToContent="WidthAndHeight">

    <Window.Resources>
        <local:IntToBlueBrushValueConverter x:Key="intToBlueBrushValueConverter"/>
    </Window.Resources>

    <StackPanel Margin="15">
        <Slider Minimum="0" Maximum="255"
                Value="{Binding BlueValue}"/>
        <Label Content="{Binding BlueValue}"/>
        <Rectangle Height="80" Width="80"
            Fill="{Binding Path=BlueValue, Converter={StaticResource intToBlueBrushValueConverter}}"/>
    </StackPanel>
</Window>

The code-behind is straightforward.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            this.DataContext = this;
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void OnPropertyChanged(string prop)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        private int blueValue;
        public int BlueValue
        {
            get { return blueValue; }
            set
            {
                if (blueValue != value)
                {
                    blueValue = value;
                    OnPropertyChanged("BlueValue");
                }
            }
        }
    }

In the value converter, we create a solid color brush whose R and G values are 0 and whose B value is derived from the integer (slider position).

    public class IntToBlueBrushValueConverter : IValueConverter
    {
        // Convert from int to System.Windows.Media.Brush
        // where brush is color with R=0, G=0 and B=int
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            int blueVal = (int)value;
            blueVal = Math.Min(Math.Max(0, blueVal), 255);

            return new SolidColorBrush(Color.FromRgb(0, 0, (byte)blueVal));
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }

At run-time, the color of the rectangle changes as we move the slider.

1138-001

1138-002

1138-003