Data Binding Expressions in WPF

Data Binding Expressions in WPF

Data binding is a powerful technology that allows data to flow between UI elements and business models. When data in the business model changes, it automatically reflects the changes to UI elements.

Last updated 10/18/2021 4:53 PM
Swati Gupta
13 min read
Category
WPF
Tags
.NET WPF Binding

There are many articles discussing the concept of binding and explaining how to use StaticResources and DynamicResources to bind properties. These concepts use data binding expressions provided by WPF. In this article, let's study the different types of data binding expressions provided by WPF.

Introduction

Data binding is a powerful technique that allows data to flow between UI elements and the business model. When data in the business model changes, it automatically reflects the changes to the UI elements.

Models Description
OneWay Source → Destination
TwoWay Source ←→ Destination
OneWayToSource Source ← Destination
OneTime Source → Destination (only once)

This can be achieved through different types of data binding expressions provided by WPF.

The types of data binding expressions are as follows:

  • DataContext Binding
  • RelativeSource Binding
  • ItemSource Binding

1. DataContext Binding

DataContext is a dependency property that acts as the default source for bindings. DataContext is inherited along the logical tree. Therefore, if you set a DataContext for a control, all child elements in the logical tree will also reference the same DataContext unless another source is explicitly specified.

Let's take an example to understand it in more detail.

1.1 Create a class Book as shown below.

public class Book
{
    public string Name
    {
        get;
        set;
    }
    public string Author
    {
        get;
        set;
    }
}

1.2 Add an XAML file DataContextBinding.XAML and place four TextBlocks as shown below.

<Grid VerticalAlignment="Center">
  <Grid.RowDefinitions>
    <RowDefinition Height="40" />
    <RowDefinition Height="40" />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="Auto" />
  </Grid.ColumnDefinitions>
  <TextBlock Text="Book Name:" FontWeight="Bold" />
  <TextBlock Grid.Column="1" />
  <TextBlock Text="Author:" FontWeight="Bold" Grid.Row="1" />
  <TextBlock Grid.Row="1" Grid.Column="1" />
</Grid>

Now, let's see how to use this DataContext property to display data.

It has two usages, as shown below.

    1. Using the expression

Used to bind directly to the DataContext.

Create an instance of the Book class, initialize its properties, and assign the Name property of the class to the DataContext property of the Window.

public partial class DataContextBinding: Window
{
    public DataContextBinding()
    {
        InitializeComponent();
        //Create the instance
        Book book = new Book();
        //initialize the properties
        book.Name = "Computer Networking";
        //Assign the Property as DataContext
        this.DataContext = book.Name;
    }
}

Since DataContext is inherited along the logical tree and the data is a book, Name is bound to the Control Window. All child elements of the Window will also reference the same object (book.Name).

To display data, bind the DataContext with the Textblock as shown below.

<TextBlock Text="Book Name:" FontWeight="Bold" />
<TextBlock Text="{Binding}" Grid.Column="1" />

Output

  1. Using the expression

Bind to the property of the DataContext.

Create an instance of the Book class, initialize its properties, and assign the instance of the class (Book) to the DataContext property of the Window.

Book book = new Book();
//initialize the properties
book.Name = "Computer Networking";
book.Author = "James F. Kurose";
//Assign the instance as DataContext
this.DataContext = book;

Now, let's see the output.

Since the binding expression is used to bind the DataContext object of type Book, the ToString() method is called, and the data is displayed as a string. To display the data in the correct format, we must bind the properties of the data object to the TextBlocks as follows:

<TextBlock Text="Book Name:" FontWeight="Bold" />
<TextBlock Text="{Binding Name}" Grid.Column="1" />
<TextBlock Text="Author:" FontWeight="Bold" Grid.Row="1" />
<TextBlock Text="{Binding Author}" Grid.Row="1" Grid.Column="1" />

The binding expression is used to bind to the Name property of the DataContext.

Output

2. RelativeSource Binding

RelativeSource is a property that sets the binding source relative to the binding target. This extension is primarily used when you need to bind one property of an element to another property of the same element.

RelativeSource has four types, as shown below.

  1. Self
  2. FindAncestor
  3. TemplatedParent
  4. PreviousData

Let's explore each one in detail.

2.1 Self

Self is used in scenarios where the binding source and binding target are the same. One property of an object is bound to another property of the same object.

For example, let's take an ellipse with the same height and width.

Add the code given below in the XAML file. The width property is bound relatively to the height property.

<Grid>
  <Ellipse
    Fill="Black"
    Height="100"
    Width="{Binding RelativeSource={RelativeSource Self},Path=Height}"
  >
  </Ellipse>
</Grid>

Output

If you change the height of the ellipse, the width will also change relatively.

2.2 FindAncestor

As the name suggests, this is used when the binding source is one of the ancestors (parents) of the binding target. Using the FindAncestor extension, you can find ancestors at any level.

Let's take an example to understand it more clearly.

Steps

Create XAML that represents the logical tree of elements given below.

<Grid Name="Parent_3">
  <StackPanel Name="Parent_2">
    <Border Name="Parent_1">
      <StackPanel x:Name="Parent_0" Orientation="Vertical">
        <button></button>
      </StackPanel>
    </Border>
  </StackPanel>
</Grid>

Now, let's use the FindAncestor extension to bind the Name property of the ancestor to the Content property of the child element button.

<Grid Name="Parent_3">
  <StackPanel
    Name="Parent_2"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"
    Width="100"
  >
    <Border Name="Parent_1">
      <StackPanel x:Name="Parent_0" Orientation="Vertical">
        <button
          Height="50"
          Content="{Binding RelativeSource={RelativeSource FindAncestor,  
AncestorType={x:Type StackPanel},  
AncestorLevel=2},Path=Name}"
        ></button>
      </StackPanel>
    </Border>
  </StackPanel>
</Grid>

Output

The combination of AncestorType "StackPanel" and AncestorLevel "2" binds the Content property of the button to the Name property of the StackPanel (Parent_2).

2.3 TemplatedParent

TemplatedParent is a property that enables you to create a control template containing a few unknown values. These values depend on the properties of the control to which the ControlTemplate is applied.

Let's take an example to understand it in more detail.

Steps

  1. Create a ControlTemplate for a button as shown below.
<Window.Resources>
  <ControlTemplate x:Key="template">
    <canvas>
      <Ellipse Height="110" Width="155" Fill="Black" />
      <Ellipse
        Height="100"
        Width="150"
        Fill="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}"
      >
      </Ellipse>
      <ContentPresenter
        Margin="35"
        Content="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"
      />
    </canvas>
  </ControlTemplate>
</Window.Resources>

In the code given above, the Fill property of the Ellipse and the Content property of the ContentPresenter depend on the property values of the control to which this template will be applied.

  1. Add a button and apply the template to it.
<button
  Margin="50"
  Background="Beige"
  Template="{StaticResource template}"
  Height="0"
  Content="Click me"
  FontSize="22"
></button>

When applying the template, the Background (Beige) of the button is bound relatively to the Fill property of the Ellipse, and the Content (Click me) is bound relatively to the Content property of the ContentPresenter. The dependent values take effect and produce the following output.

Output

2.4 PreviousData

This is the least used relative mode. It comes into play when data is analyzed, and we need to represent the change in value relative to the previous data.

Let's take an example to understand it in more detail.

Steps

  1. Create a class Data and implement the INotifyPropertyChanged interface as shown below.
public class Data: INotifyPropertyChanged
{
    public int DataValue
    {
        get;
        set;
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string PropertyName)
    {
        if (null != PropertyChanged)
        {
            PropertyChanged(this,
                new PropertyChangedEventArgs(PropertyName));
        }
    }
}
  1. Create a list of type Data and assign it as DataContext.
public RelativeSourcePreviousData()
{
    InitializeComponent();
    List < Data > data = new List < Data > ();
    data.Add(new Data()
    {
        DataValue = 60
    });
    data.Add(new Data()
    {
        DataValue = 100
    });
    data.Add(new Data()
    {
        DataValue = 120
    });
    this.DataContext = data;
}
  1. Add an ItemsControl in the XAML file.
<ItemsControl ItemsSource="{Binding}"></ItemsControl>
  1. Create an ItemsPanel template for it as follows.
<ItemsControl ItemsSource="{Binding}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <StackPanel Orientation="Vertical" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>
  1. Now, to properly represent the data, create a DataTemplate as follows.
<ItemsControl.ItemTemplate>
  <DataTemplate>
    <StackPanel Orientation="Horizontal">
      <Grid Margin="30,20,0,0">
        <Rectangle Width="80" Height="{Binding DataValue}" Fill="Blue" />
        <TextBlock
          Foreground="White"
          Margin="35,0,0,0"
          Text="{Binding DataValue}"
        ></TextBlock>
      </Grid>
      <TextBlock Margin="30,20,0,0" Text="Previous Data:"></TextBlock>
      <TextBlock
        VerticalAlignment="Center"
        Margin="5,20,0,0"
        Text="{Binding  
             RelativeSource={RelativeSource PreviousData}, Path=DataValue}"
      />
    </StackPanel>
  </DataTemplate>
</ItemsControl.ItemTemplate>

Output

The height of the blue box is the value of the item in the list, and the old data is displayed on the right. The first item's value is "60". Therefore, the first item has no old value.

3. ItemSource Binding

Used when dealing with collections. Using this binding expression, you can easily read the properties of the SelectedItem. The slash is a special operator used to handle the current item in a collection.

Three expressions are given below.

  1. {Binding / }
  2. {Binding Collection / }
  3. {Binding Collection / Property}

3.1 {Binding / }

This expression is used to bind to the current item in the DataContext.

Let's take an example:

In the example given below, the DataContext is a collection of countries of type string and is bound to a ListBox.

Steps

  1. Create a Countries class and add a GetCountriesName() method that returns a collection of countries of string data type, as shown below.
public class Countries
{
    public static List <string> GetCountriesName()
    {
        List <string> countries = new List <string> ();
        foreach(CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
        {
            RegionInfo country = new RegionInfo(culture.LCID);
            if (!countries.Contains(country.EnglishName))
                countries.Add(country.EnglishName);
        }
        countries.Sort();
        return countries;
    }
}
  1. Add an XAML file, a ListBox, and a TextBlock as shown below.
<DockPanel Name="Collection">
  <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
  </ListBox>
  <TextBlock DockPanel.Dock="Top" />
</DockPanel>
  1. Create an instance of the Countries class and assign the Countries collection as DataContext.
public CurrentItemCollection()
{
    InitializeComponent();
    Countries countries = new Countries();
    this.DataContext = countries.GetCountriesName()
}
  1. Bind the Text property of the TextBlock to bind it to the currently selected item of the collection, as follows.
<TextBlock DockPanel.Dock="Top" Text="{Binding /}" />

Output

Once a list item is selected, it displays the selected country on the right.

3.2 {Binding Collection /}

This expression is used to bind to the current item of a collection property in the DataContext.

For example,

DataContext is the Countries class

Collection property is CountriesList, which is bound to the ListBox.

Steps

  1. Use a similar Countries class created above, but with a slight difference. Create a method with a return type of RegionInfo.
public static List <RegionInfo> GetCountries()
{
    List <RegionInfo> countries = new List <RegionInfo> ();
    foreach(CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
    {
        RegionInfo country = new RegionInfo(culture.LCID);
        if (countries.Where(p => p.Name == country.Name).Count() == 0)
            countries.Add(country);
    }
    return countries.OrderBy(p => p.EnglishName).ToList();
}
  1. Add a CountriesList property of type RegionInfo.
private List <RegionInfo> countries = null;
public List <RegionInfo> CountriesList
{
    get
    {
        if (countries == null)
            countries = GetCountries();
        return countries;
    }
}

Below is a screenshot of the values in the CountriesList collection.

  1. Assign the Countries class as DataContext and bind the ListBox to the CountriesList property of the DataContext.
<Window.Resources>
  <vm:Countries x:Key="Countries"></vm:Countries>
</Window.Resources>
<Grid>
  <DockPanel Name="Collection" DataContext="{StaticResource Countries}">
    <ListBox
      ItemsSource="{Binding CountriesList}"
      IsSynchronizedWithCurrentItem="True"
    >
      <ListBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding EnglishName}"></TextBlock>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </DockPanel>
</Grid>
  1. To get the current item of the CountriesList property, bind the Text property of the TextBlock as follows.
<TextBlock DockPanel.Dock="Top" Text="{Binding CountriesList/}" HorizontalAlignment="Center" FontSize="16" VerticalAlignment="Center" />

Output

The right side displays the current item (CountriesList) of the collection in the DataContext (CountriesList).

3.3 {Binding Collection / Property}

This expression is used to bind to the property of the current item of a collection in the DataContext.

For example, if you need to get a specific property of the current item of the CountriesList collection.

In this example, I want to display the value of the property "EnglishName".

To do this, bind the Text property of the TextBlock as follows.

<TextBlock DockPanel.Dock="Top" Text="{Binding CountriesList/EnglishName}" />

Output

Now, when an item in the list is selected, it displays the value of the property "EnglishName".

Conclusion

I have covered all the data binding expressions in detail. I hope this helps you understand the concept of binding and the expressions provided by WPF.


Keep Exploring

Related Reading

More Articles
Same category / Same tag 9/13/2025

Migration Series from WPF to Avalonia: Why I Must Migrate My WPF Application to Avalonia

In the past few years, our host computer software has mainly been developed using WPF and WinForm . These technologies work well on the Windows platform and have accompanied us from small-scale trial production to the current stage of large-scale delivery. However, with business development and changes in customer requirements, the single Windows technology stack has gradually become a hurdle we must overcome.

Continue Reading
Same category / Same tag 1/26/2025

Implementing Internationalization in WPF Using Custom XML Files

This article details the method of implementing internationalization in WPF applications using custom XML files, including installing the necessary NuGet packages, dynamically retrieving the language list, dynamically switching languages, using translated strings in code and XAML interfaces, and provides a source code link to help developers easily achieve internationalization in WPF applications.

Continue Reading