There are lot of articles discussing about the concept of Binding and explaining how to bind properties using StaticResources and DynamicResources. These concepts use the DataBinding expressions provided by WPF. In this article let’s explore the different types of DataBinding Expressions provided by WPF.
Introduction
DataBinding is a powerful technique that allows the flow of data between UI Elements and Business model. It reflects the changes automatically to UI element as soon as the data in Business model changes.
Modes of DataBinding
Modes |
Description |
OneWay |
Source → Destination |
TwoWay |
Source ←→ Destination |
OneWayToSource |
Source ← Destination |
OneTime |
Source → Destination (only once) |
This can be achieved by different types of DataBinding Expression provided by WPF.
Types of DataBinding Expressions
- DataContext Binding
- RelativeSource Binding
- Current Item of Collection Binding
DataContext Binding
DataContext is a Dependency property which is a default source of binding. Datacontext is inherited along the logical tree. So, if you set a datacontext to a control all the child elements in logical tree will also refer to the same datacontext, unless and until another source is specified explicitly.
Let’s take an example to understand it in more detail
- Create a class
Book
as shown below
public class Book
{
public string Name { get; set; }
public string Author { get; set; }
}
- Add a 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 this
DataContext
Property is used to display data
It is used in two ways:
- Using {Binding} expression - Used to bind the
DataContext
directly.
Create an instance of class Book
, initialize its properties and assign Name
property of class to the DataContext
property of Window.
public partial class DataContextBinding : Window
{
public DataContextBinding()
{
InitializeComponent();
Book book = new Book();
book.Name = "Computer Networking";
this.DataContext = book.Name;
}
}
As the Datacontext
is inherited along the logical tree and the data book. Name
is bound to Control Window, All the child elements of Window will also refer to same object (book.Name
)
To Display the data, bind the DataContext
with the Textblock
as below
<TextBlock Text="Book Name:" FontWeight="Bold"/>
<TextBlock Text="{Binding}" Grid.Column="1" />
OUTPUT
- Using {Binding Property} expression - Binds the Property of Datacontext.
Create an instance of class Book
, initialize its properties and assign instance of class (book) to the DataContext
property of Window
.
Book book = new Book();
book.Name = "Computer Networking";
book.Author = "James F. Kurose";
this.DataContext = book;
Now let’s see the output.
As the Binding expression {Binding} is used to bind the DataContext object of type Book
, ToString()
method is called on it and the data is shown as string.
To Display data in proper format we have to bind the properties of data object with the Textblocks as below:
<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"/>
Binding expression {Binding Name} is used to bind the Name
property of DataContext
bound.
OUTPUT
RelativeSource Binding
RelativeSource
is property which sets the binding source with the relative relation to binding target. This extension is majorly used when you have to bind one property of the element to another property of same element.
There are four types of RelativeSource
:
- Self
- FindAncestor
- TemplatedParent
- PreviousData
Let’s explore them one-by-one in detail.
Self
Self is used in a scenario when the Binding source and the binding target are same. One property of the object is bind with another property of same object.
For example: Let’s take an Ellipse with same height and width
Add the below code in the XAML file. Width
property is bind with height property relatively.
<Grid>
<Ellipse Fill="Black" Height="100"
Width="{Binding RelativeSource={RelativeSource Self},Path=Height}">
</Ellipse>
</Grid>
OUTPUT
If the Height of Ellipse is changed the Width will also change relatively.
FindAncestor
As the name says, this is used when binding source is one of the Ancestors (Parents) of the binding target. Using FindAncestor
extension you can find ancestor up to any level.
Let’s take an example to understand it more clearly.
Steps
Create a XAML representing the below logical tree of elements
<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 FindAncestor
extension to bind the Name
Property of ancestors to the Content Property of 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>
OUPUT
AncestorType "StackPanel
" in combination with AcestorLevel as "2
" binds the Content property of button with Name
Property of StackPanel (Parent_2
).
TemplatedParent
TemplatedParent
is Property which enables you to create a Control template with few unknown values. These values depend on properties of control that ControlTemplate
is applied to.
Let’s take an example to understand it in more detail
Steps
- Create a
ControlTemplate
for 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 above code sample Fill
Property of Ellipse
and Content
Property of ContentPresenter
is dependent on the values of properties of 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>
As the template is applied, the Background (Beige) of Button is relatively bind with Fill
Property of Ellipse
and Content
(Click me) is relatively bind with Content
property of ContentPresenter
. The dependent values are evaluated and gives the below output.
OUTPUT
PreviousData
This is the least used mode of RelativeSource
. This comes into picture when a data is analyzed and we need to represent the change in values with respect to previous data.
Let’s take an example to understand it in more detail
Steps
- Create a class
Data
and implement 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 to type
Data
and assign it as the 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
in XAML file
<ItemsControl ItemsSource="{Binding}"></ItemsControl>
- Create
ItemsPanel
Template for it as below
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
- Now for proper representation of data create
DataTemplate
as 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 Blue boxes is the value of the items in List and the Previous Data is shown at right with respect to boxes. The first value of the item is "60". Therefore the Previous data shows no value for first item.
Current Item of Collection Binding
This is used when you are working with Collection. You can read the properties of SelectedItem very easily using this binding expression. The slash is special operator which used to deal with current item in the collection.
There three kind of expressions used
- {Binding / }
- {Binding Collection / }
- {Binding Collection / Property}
{Binding / }
This expression is used to bind the current item in the DataContext.
Let’s take an example:
In below example DataContext
is Collection of Countries of string type and same is bind with the Listbox
Steps
- Create a class
Countries
and add a method GetCountriesName()
that returns the collection of Countries of string data type as 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 a XAML file add
Listbox
and TextBlock
as shown below
<DockPanel Name="Collection">
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
</ListBox>
<TextBlock DockPanel.Dock="Top"/>
</DockPanel>
- Create the instance of class
Countries
and assign the Collection of Countries
as the DataContext
public CurrentItemCollection()
{
InitializeComponent();
Countries countries = new Countries();
this.DataContext = countries.GetCountriesName()
}
- Bind the
Text
property of TextBlock to bind it with current selected item of collection as below
<TextBlock DockPanel.Dock="Top" Text="{Binding /}" />
OUTPUT
As soon as the item is selected Item, it displays the selected country on right.
{Binding Collection / }
This expression is used to bind the current item of the Collection Property within the DataContext.
Example
DataContext is Countries
class
Collection Property is CounriesList
which is bind with the ListBox
Steps
- Use the same class
Countries
created above with a slight difference. 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
CountriesList
Property of type RegionInfo
private List<RegionInfo> countries = null;
public List<RegionInfo> CountriesList
{
get
{
if (countries == null)
countries = GetCountries();
return countries;
}
}
Below is screenshots of values in CountriesList
collection
- Specify the class
Countries
as the DataContext
and bind the Listbox
with the CountriesList
Property of 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 evaluate the current item of
CountriesList
property binds the Text
property of TextBlock
as below.
<TextBlock DockPanel.Dock="Top"
Text="{Binding CountriesList/}" HorizontalAlignment="Center" FontSize="16" VerticalAlignment="Center"
/>
OUTPUT
Right side displays the current item of the Collection (CountriesList) within the DataContext (Countries)
{Binding Collection / Property}
This expression is used to bind the Property of current item of Collection within the DataContext
For example, if we have to evaluate a specific property of current item of CountriesList collection.
In this example I want to show value of property "EnglishName
".
To do so, bind the Text Property of TextBlock as below
<TextBlock DockPanel.Dock="Top"
Text="{Binding CountriesList/EnglishName}" />
Output
Now it shows the value of Property "EnglishName
" as the item in the list is selected.
Conclusion
I have covered all the data binding expressions is detail. I hope this helps you to understand the concept of Binding and expressions provided by WPF.