wpfIntroduction to WPF Data Binding

Syntax

  • {Binding PropertyName} is equivalent to {Binding Path=PropertyName}
  • {Binding Path=SomeProperty.SomeOtherProperty.YetAnotherProperty}
  • {Binding Path=SomeListProperty[1]}

Parameters

ParameterDetails
PathSpecifies the path to bind to. If unspecified, binds to the DataContext itself.
UpdateSourceTriggerSpecifies when the binding source has its value updated. Defaults to LostFocus. Most used value is PropertyChanged.
ModeTypically OneWay or TwoWay. If unspecified by the binding, it defaults to OneWay unless the binding target requests it to be TwoWay. An error occurs when TwoWay is used to bind to a readonly property, e.g. OneWay must be explicitly set when binding a readonly string property to TextBox.Text.
SourceAllows for using a StaticResource as a binding source instead of the current DataContext.
RelativeSourceAllows for using another XAML element as a binding source instead of the current DataContext.
ElementNameAllows for using a named XAML element as a binding source instead of the current DataContext.
FallbackValueIf the binding fails, this value is provided to the binding target.
TargetNullValueIf the binding source value is null, this value is provided to the binding target.
ConverterSpecifies the converter StaticResource that is used to convert the binding's value, e.g. convert a boolean to a Visibility enum item.
ConverterParameterSpecifies an optional parameter to be provided to the converter. This value must be static and cannot be bound.
StringFormatSpecifies a format string to be used when displaying the bound value.
Delay(WPF 4.5+) Specifies a Delay in milliseconds for the binding to update the BindingSource in the ViewModel. This must be used with Mode=TwoWay and UpdateSourceTrigger=PropertyChanged to take effect.

Remarks

UpdateSourceTrigger

By default, WPF updates the binding source when the control loses focus. However, if there is only one control that can get focus -- something that's common in examples -- you will need to specify UpdateSourceTrigger=PropertyChanged for the updates to work.

You will want want to use PropertyChanged as the trigger on many two-way bindings unless updating the binding source on every keystroke is costly or live data validation is undesirable.

Using LostFocus has an unfortunate side effect: pressing enter to submit a form using a button marked IsDefault does not update the property backing your binding, effectively undoing your changes. Fortunately, some workarounds exist.

Please also note that, unlike UWP, WPF (4.5+) also has the Delay property in bindings, wich might just be enough for some Bindings with local-only or simple minor intelligence settings, like some TextBox validations.

Convert a boolean to visibility value

This example hides the red box (border) if the checkbox is not checked by making use of an IValueConverter.

Note: The BooleanToVisibilityConverter used in the example below is a built-in value converter, located in the System.Windows.Controls namespace.

XAML:

<Window x:Class="StackOverflowDataBindingExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="VisibleIfTrueConverter" />
    </Window.Resources>
    <StackPanel>
        <CheckBox x:Name="MyCheckBox"
                  IsChecked="True" />
        <Border Background="Red" Width="20" Height="20"
                Visibility="{Binding Path=IsChecked,ElementName=MyCheckBox, Converter={StaticResource VisibleIfTrueConverter}}" />
    </StackPanel>
</Window>

Defining the DataContext

In order to work with bindings in WPF, you need to define a DataContext. The DataContext tells bindings where to get their data from by default.

<Window x:Class="StackOverflowDataBindingExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StackOverflowDataBindingExample"
        xmlns:vm="clr-namespace:StackOverflowDataBindingExample.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <vm:HelloWorldViewModel />
    </Window.DataContext>
    ...
</Window>

You can also set the DataContext through code-behind, but it is worth noting that XAML IntelliSense is somewhat picky: a strongly-typed DataContext must be set in XAML for IntelliSense to suggest properties available for binding.

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new HelloWorldViewModel();
    }
}

While there are frameworks to help you define your DataContext in a more flexible way (e.g. MVVM Light has a viewmodel locator that uses inversion of control), we use the quick and dirty method for the purposes of this tutorial.

You can define a DataContext for pretty much any visual element in WPF. The DataContext is generally inherited from ancestors in the visual tree unless it has been explicitly overridden, e.g. inside a ContentPresenter.

Implementing INotifyPropertyChanged

INotifyPropertyChanged is an interface used by binding sources (i.e. the DataContext) to let the user interface or other components know that a property has been changed. WPF automatically updates the UI for you when it sees the PropertyChanged event raised. It is desirable to have this interface implemented on a base class that all of your viewmodels can inherit from.

In C# 6, this is all you need:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

This allows you to invoke NotifyPropertyChanged in two different ways:

  1. NotifyPropertyChanged(), which will raise the event for the setter that invokes it, thanks to the attribute CallerMemberName.
  2. NotifyPropertyChanged(nameof(SomeOtherProperty)), which will raise the event for SomeOtherProperty.

For .NET 4.5 and above using C# 5.0, this can be used instead:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged([CallerMemberName] string name = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

In versions of .NET prior to 4.5, you have to settle for property names as string constants or a solution using expressions.


Note: It is possible to bind to a property of a "plain old C# object" (POCO) that does not implement INotifyPropertyChanged and observe that the bindings work better than expected. This is a hidden feature in .NET and should probably be avoided. Especially as it will cause memory leaks when the binding's Mode is not OneTime (see here).

Why does the binding update without implementing INotifyPropertyChanged?

Bind to property of another named element

You can bind to a property on a named element, but the named element must be in scope.

<StackPanel>
    <CheckBox x:Name="MyCheckBox" IsChecked="True" />
    <TextBlock Text="{Binding IsChecked, ElementName=MyCheckBox}" />
</StackPanel>

Bind to property of an ancestor

You can bind to a property of an ancestor in the visual tree by using a RelativeSource binding. The nearest control higher in the visual tree which has the same type or is derived from the type you specify will be used as the binding's source:

<Grid Background="Blue">
    <Grid Background="Gray" Margin="10">
        <Border Background="Red" Margin="20">
            <StackPanel Background="White" Margin="20">
                <Button Margin="10" Content="Button1" Background="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}}}" />
                <Button Margin="10" Content="Button2" Background="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type FrameworkElement}}}" />
            </StackPanel>
        </Border>
    </Grid>
</Grid>

In this example, Button1 has a gray background because the closest Grid ancestor has a gray background. Button2 has a white background because the closest ancestor derived from FrameworkElement is the white StackPanel.

RelativeSource binding example

Binding multiple values with a MultiBinding

The MultiBinding allows binding multiple values to the same property. In the following example multiple values are bound to the Text property of a Textbox and formatted using the StringFormat property.

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="{}{0} {1}">
            <Binding Path="User.Forename"/>
            <Binding Path="User.Surname"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

Apart from StringFormat, an IMultiValueConvertercould also be used to convert the values from the Bindings to one value for the MultiBinding's target.

However, MultiBindings cannot be nested.