There are many articles that discuss the concept of binding and explain how to bind properties using StaticResources and DynamicResources. These concepts use data-binding expressions provided by WPF. In this article, let's examine the different types of data-binding expressions provided by WPF.
introduced
Data binding is a powerful technology that allows data to flow between UI elements and business models. When data changes in the business model, it automatically reflects the changes to UI elements.
| Models | Description |
|---|---|
| OneWay | Source → Destination |
| TwoWay | Source ←→ Destination |
| OneWayToSource | Source ← Destination |
| OneTime | Source → Destination (only once) |
This can be achieved through the 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 dependent attribute that is the default source for binding. Datacontext inherits along the logical tree. Therefore, if you set up a DataContext to control all child elements in the logical tree, it will also reference the same DataContext unless and until another source is explicitly specified.
Let's give 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 添加一个 XAML 文件DataContextBinding.XAML并放置四个 TextBlock,如下所示。
<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 uses, as shown below.
-
- Use the expression
Used to directly bind DataContext.
Create an instance of the class Book, initialize its properties, and assign the class's Name property 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;
}
}
Because the DataContext is inherited along the logical tree and the data book, the Name is bound to the Control Window. All child elements of Window will also reference the same object (book.Name).
To display data, bind the DataContext to Textblock, as shown below.
<TextBlock Text="Book Name:" FontWeight="Bold" />
<TextBlock Text="{Binding}" Grid.Column="1" />
output

- Use the expression
Bind the properties of Datacontext.
Create an instance of class Book, initialize its properties, and assign the instance of class (Book) to the DataContext property of 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 look at the output.

Since the binding expression is used to bind a DataContext object of type Book, the ToString () method is called and the data is displayed as a string. In order to display data in the correct format, we must bind the properties of the data object to TextBlock, 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 the Name property of the DataContext binding.
output

2. RelativeSource binding
RelativeSource is a property that uses a relative relationship to bind the source to bind the target. This extension is mainly used when one attribute of an element must be bound to another attribute of the same element.
There are four types of RelativeSources, as shown below.
- Self
- FindAncestor
- TemplatedParent
- PreviousData
Let's discuss them in detail one by one.
2.1 Self
Self is used in scenarios where the source and 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 to the XAML file. The width attribute is relatively bound to the height attribute.
<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 option is used when the binding source is one of the ancestors (parents) of the binding target. Using the FindAnchor extension, you can find ancestors at any level.
Let's give an example to understand it more clearly.
step
Create XAML that represents a logical tree of the 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 FindAnchor extension to bind the ancestor's Name attribute to the sub-element button's Content attribute.
<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 being "StackPanel" and AcestorLevel being "2" binds the content attribute of button to the Name attribute of StackPanel (Parent_2).
2.3 TemplatedParent
TemplatedParent is a property that allows you to create a control template that contains a small number of unknown values. These values depend on the properties of the control to which ControlTemplate is applied.
Let's give an example to understand it in more detail
step
- Create a ControlTemplate for the 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 the 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 relatively bound to the Fill property of the ellipse, and the Content (Click me) is relatively bound to the Content property of the ContentPresenter. Dependent values take effect and give the following output.
output

2.4 PreviousData
This is the relatively least used method. This occurs when the data is analyzed, and we need to express the changes in values relative to previous data.
Let's give an example to understand it in more detail.
step
- Create a class Data and implement the INotifyPropertyChanged interface as follows
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 specify 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 ItemsControl to 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, in order to correctly represent the data, create a DataTemplate as shown below.
<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 old data is displayed on the right. The first value of this item is "60". Therefore, the first term has no old value.
3. ItemSource binding
Used when processing collections. Using this binding expression, you can read the properties of SelectedItem very easily. Slash is a special operator used to handle the current item in the collection.
Three expressions are given below.
- {Binding / }
- {Binding Collection / }
- {Binding Collection / Property}
3.1 {Binding / }
This expression is used to bind 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 the Listbox.
step
- Create a Countries class and add a GetCountries Name () method that returns a collection of countries with the 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 TextBlock, as shown below.
<DockPanel Name="Collection">
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
</ListBox>
<TextBlock DockPanel.Dock="Top" />
</DockPanel>
- Create an instance of the class Countries and specify 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 in the collection, as shown below.
<TextBlock DockPanel.Dock="Top" Text="{Binding /}" />
output

Once a list item is selected, it displays the selected country/region on the right.
3.2 {Binding Collection /}
This expression is used to bind the current item of the collection property in the DataContext.
example,
DataContext is the Countries class
The Collection property is CountryList, which is bound to the ListBox.
step
- Use a similar country class created above, only slightly different. Create a method with return type 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 CountryList 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 CountryList collection.

- Specify the class Countries as DataContext, and bind the Listbox to the CountryList 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 calculate the current entry of the CountryList property, bind the Text property of TextBlock, as shown below.
<TextBlock DockPanel.Dock="Top" Text="{Binding CountriesList/}" HorizontalAlignment="Center" FontSize="16" VerticalAlignment="Center" />
output

The right side displays the current item (CountryList) of the collection in the DataContext (CountryList).
3.3 {Binding Collection / Property}
This expression is used to bind the properties of the current item of the collection in the DataContext.
For example, if specific properties of the current item of the CountryList collection must be calculated.
In this example, I want to display the value of the property "EnglishName".

To do this, bind the Text property of TextBlock, as shown below.
<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 detailed all data binding expressions. I hope this will help you understand the concept of binding and the expressions provided by WPF.
- Author: Swati Gupta
- Original title: DataBinding Expressions In WPF
- Original link: www.c-sharpcorner.com/article/data-binding-expression-in-wpf/
- Editor: The wolf at the end of the desert