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.
-
- 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

- 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.
- Self
- FindAncestor
- TemplatedParent
- 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
- 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.
- 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
- 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));
}
}
}
- 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;
}
- Add an ItemsControl in the XAML file.
<ItemsControl ItemsSource="{Binding}"></ItemsControl>
- Create an ItemsPanel template for it as follows.
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
- 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.
- {Binding / }
- {Binding Collection / }
- {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
- 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;
}
}
- 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>
- 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()
}
- 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
- 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();
}
- 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.

- 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>
- 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.
- Author: Swati Gupta
- Original Title: DataBinding Expressions In WPF
- Original Link: https://www.c-sharpcorner.com/article/data-binding-expression-in-wpf/
- Editor: Desert at the End of the Wolf's Tail