WPF – Data Binding

One ring to bring them all, and in the darkness bind them.

I’ve had a few requests to cover WPF data-binding in more detail, so in this post I’ll go back to basics and provide examples of the various forms of data-binding possible within a WPF application.

In WPF, data-binding is a decoupled method of creating automated links between UI controls and the application data, without having to explicitly set control property values in code.

Data-Bindings are usually defined in the xaml file, and take the form

<Target-Property>="{Binding Path=<Source-Property><Binding-Flags>}"

where <Target-Property> is a dependency property on a dependency object instance (usually a visual control), and <Souce-Property> is a property of a data object – usually the control’s data context, although this is not always the case. If the source object implements INotifyPropertyChanged then the target property will be automatically updated whenever the source property value changes, without having to write any additional code. The optional <Binding-Flags> element adds additional parameters to control the binding. In simple bindings the Path= element is not required.

The demo application for this post consists of a single View class, with a ViewModel class (that indirectly inherits from MVVMLight’s ObservableObject to implement INotifyPropetyChanged) set as its DataContext.

Simple Data-Binding

The most basic form of binding is to simple static property, that has just a get accessor.

public string ViewTitle => "WPF Data-Binding Demo";
<Window
    ...
    Title="{Binding ViewTitle}">

The title property of the window will be set once, when the View’s data context is set as part of the loading routine, and that’s it.

I’m not suggesting that every caption or other static text in your application should be defined as properties of the ViewModel, but there may be occasions, such as a collection of multiple related item types displayed in a DataGrid control where having such a static text value as an item type identifier is useful.

For such binding, the source property type does not have to exactly match that of the target property. For example, you could bind a string property (e.g. TextBlock.Text) to a source property of any value or reference type, and WPF will automatically convert the value to a string behind the scenes. I’ll cover the case where such automatic conversion is not possible later in this post.

Updating a Data-Binding from the ViewModel

If you want a property where changes in the source property are reflected in the binding target, then the source object (in this case the ViewModel) has to implement INotifyPropertyChanged. My MVVM framework of choice – MVVMLight – provides a simple methodology for this as part of its ObservableObject class. I covered this Set() method in more detail in a previous post.

private string _readOnlyProp;

public string ReadOnlyProp
{
    get => _readOnlyProp;
    private set => Set(nameof(ReadOnlyProp), ref _readOnlyProp, value);
}

Due to the private accessor, the ReadOnlyProp value can only be set from within the ViewModel – in this case when the user clicks the button, thus invoking the SetReadOnlyPropCommand.

However, the binding statement takes exactly the same form as the previous example – nothing further is required to get bindings to automatically refresh.

<TextBlock
    ...
    Text="{Binding ReadOnlyProp}" />

Updating a Data-Binding from the View (Two-Way Binding)

The next level of complexity is when two-way binding is required – updating the bound property from a UI control, a few more binding options may be required. In this case we have a TextBox control that will update the value of the TextToDuplicate property as the user types.

In previous versions of WPF, it was necessary to specify a binding mode for such cases, but Microsoft has now updated most control class definitions to include the appropriate default binding flag values in dependency property definitions.

public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register(nameof (Text),
        typeof (string),
        typeof (TextBox),
        (PropertyMetadata) new FrameworkPropertyMetadata((object) string.Empty,
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
            new PropertyChangedCallback(TextBox.OnTextPropertyChanged),
            new CoerceValueCallback(TextBox.CoerceText),
            true,
            UpdateSourceTrigger.LostFocus));

If you ever come across a binding that doesn’t update as you would expect, then it may still be necessary to explicitly include a mode setting in the binding definition for some control types.

For anyone creating their own custom controls, I would suggest adding a default binding mode to dependency property definitions when two-way binding is likely to be required.

You could just define the binding as before

<TextBox
    ...
    Text="{Binding TextToDuplicate}" />

and this would work fine. However, as can be seen from the code snippet above, the default update trigger for TextBox.Text is LostFocus. This means that the binding won’t be refreshed until the user switches focus to a different control on the View.

If you want the bound property to be updated without having to leave the control, then this functionality can be included by adding a flag to the binding statement.

<TextBox
    ...
    Text="{Binding TextToDuplicate, UpdateSourceTrigger=PropertyChanged}" />

Now the TextToDuplicate property will be updated each time the TextBox.Text property changes.

If you’re kicking off some compuationally expensive operation after a property value is updated (such as a sugestion for a search engine query), then you might not want to do this after every single key stoke. To address such a requirement, the binding statement can also have a delay clause, which will buffer any updates until the specified time period is reached since the last update.

<TextBox
    ...
    Text="{Binding TextToDuplicate, UpdateSourceTrigger=PropertyChanged, Delay=500}" />

Data-Binding Without a ViewModel

It is also possible to create a binding that doesn’t use the control’s data context at all. Instead you can bind directly to a property of another named control. An example of this would be if you have a ToggleButton used to show / hide another section of the UI, then there’s no need to create a boolean flag property on the ViewModel to achieve this.

<TextBlock
    ...
    Text="{Binding ElementName=TheTextBox, Path=Text}" />

Note that as we’re binding directly to the property value the update is immediate – the delay clause on the data-binding for TextBox.Text is ignored.

Value Converters

The next case to consider is when the source and target properties are not of the same type. One obvious example of this is to avoid having View related classes inside the ViewModel. In the demo application, there is a requirement to change the colour of a displayed element – a data item’s description must be at least 30 characters long to be valid. However in WPF, a control’s background property is defined as a Brush object, not a simple Color value. In MVVM we don’t want to have such a UI class in the ViewModel though. The solution is to use a converter object – a class that implements the IValueConverter interface. This will take a value of the source property type and convert it the the corresponding type for the target property. A color value is just a number, so there’s no issue with having that in the ViewModel. The converter between Color and Brush values is defined as …

public class perColorToSolidColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is Color color)
            return new SolidColorBrush(color);

        return DefaultValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var brush = value as SolidColorBrush;
        return brush?.Color;
    }

    public SolidColorBrush DefaultValue { get; } = Brushes.Fuchsia;
}

Converters are defined as a resource, either within an individual control / view or in App.Xaml if you want to only have a single instance of each converter type throughout the whole application.

<Window.Resources>
    <conv:perColorToSolidColorBrushConverter x:Key="ColorToBrushConverter" />
</Window.Resources>

This resource is then referenced as a parameter in the binding statement.

<Rectangle
    ...
    Fill="{Binding DescriptionValidColor, Converter={StaticResource ColorToBrushConverter}}" />

Probably the most common converter class used in WPF is BooleanToVisibilityConverter. This native .Net class converts between a Boolean (true / false) value and a Visibility enum (Visible / Hidden / Collapsed) value.

Relative Data-Binding

The last case I’m going to cover is when you want to bind to a property of an object other than a control’s DataContext. In the demo application, a collection of DataItemViewModel instances is displayed in a DataGrid control. The DataContext for each row of the grid (and therefore each of its sub components) will be the individual data item. This works fine for the other controls, but not for the delete button’s command – DeleteDataItemCommand is a property of the main ViewModel, not an individual data item. To reference this command property, we have to use relative binding.

<Button
    ...
    Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.DeleteDataItemCommand}"
    CommandParameter="{Binding}">

The binding mechanism will step back through the visual tree until it finds an object of type Window, and then use the DeleteDataItemCommand of its DataContext (a MainViewModel instance) as the binding source.

Note also the binding for CommandParameter. Using "{Binding}" just says use the current DataContext (in this case a DataItemViewModel instance) as the binding source.

Further examples of relative source binding can be found in the code from my previous posts

Busy Spinner

<Canvas 
    ...
    Width="{Binding Path=Diameter, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctrl:perBusySpinner}}}">

AncestorType can be any element in the visual tree, not just the top level window.

Xaml Icon Host

<!--  Set the DataContext to "self" so that the Xaml Icon item can bind to the Foreground and BorderBrush properties  -->
<ContentControl
    ...
    DataContext="{Binding RelativeSource={RelativeSource Self}}" >

RelativeSource Self is the ContentControl itself.

Colour Spaces – HSL based colour picker

<TextBox 
    ...
    Text="{Binding Path=Luminosity, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}" />

TemplatedParent is the item that a control template is being applied to.

As usual the code samples for this blog are available on Github, along with my own personal C# / WPF library.

If you find this article useful, or you have any other feedback, please leave a comment below.

Leave a Reply

Your email address will not be published.