There are a lot of articles which discuss the concept of Binding and explain how to bind the properties, using StaticResources and DynamicResources. These concepts use the DataBinding expressions provided by WPF. In this article, let’s explore the different types of Data Binding Expressions provided by WPF.
Introduction
Data Binding is a powerful technique, which 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 Data Binding
Modes | Description |
OneWay | Source → Destination |
TwoWay | Source ←→ Destination |
OneWayToSource | Source ← Destination |
OneTime | Source → Destination (only once) |
This can be achieved by different types of Data Binding Expression provided by WPF.
Types of Data Binding Expressions are given below.
- 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. Hence, if you set a datacontext to control all the child elements in the logical tree it 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 the data.
It is used in two ways, which are given below.
- Using {Binding} expression
Used to bind the DataContext directly.
Create an instance of class Book, initialize its properties and assign the Name property of the 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 shown 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 a string.To display the data in proper format, we have to bind the properties of data object with the Textblocks, as shown 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 a property, which sets the binding source with the relative relation to bind the target. This extension is majorly used when you have to bind one property of the element to another property of the same element.
There are four types of RelativeSource, which are given below.
- 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 bound with another property of the same object.
For example- let’s take an Ellipse with same height and width.
Add the code given below in XAML file. Width property is bound 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 the binding source is one of the Ancestors (Parents) of the binding targets. Using FindAncestor extension, you can find ancestor up to any level.
Let’s take an example to understand it more clearly.
Steps
Create XAML, which 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 FindAncestor extension to bind the Name Property of the 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 the Name property of StackPanel (Parent_2).
TemplatedParent
TemplatedParent is a property, which enables you to create a Control template with few unknown values. These values depend on the 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 code given above, 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 bound with Fill Property of Ellipse and Content (Click me) is relatively bound with Content property of ContentPresenter. The dependent values are evaluated and gives the output given below.
OUTPUT
PreviousData
This is the least used mode of RelativeSource. This comes into the picture when 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 shown below.
- <ItemsControl ItemsSource="{Binding}">
- <ItemsControl.ItemsPanel>
- <ItemsPanelTemplate>
- <StackPanel Orientation="Vertical" />
- </ItemsPanelTemplate>
- </ItemsControl.ItemsPanel>
- </ItemsControl>
- Now, for proper representation of data, create 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 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 a special operator, which is used to deal with current item in the collection.
There are three kind of expressions, which are used given below.
- {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 the example given below, DataContext is a collection of countries of string type and same is bound with the Listbox.
Steps
- Create a class Countries and add a method GetCountriesName(), which returns the 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 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 DataContext.
- public CurrentItemCollection()
- {
- InitializeComponent();
- Countries countries = new Countries();
- this.DataContext = countries.GetCountriesName()
- }
- Bind the Text property of TextBlock to bind it with the current selected item of the collection, as shown below.
- <TextBlock DockPanel.Dock="Top" Text="{Binding /}" />
OUTPUT
As soon as the item is selected Item, it displays the selected country on the right.
{Binding Collection / }
This expression is used to bind the current item of the Collection property within the DataContext.
For Example,
DataContext is Countries class
Collection property is CounriesList, which is bound 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 the screenshot 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, bind the Text property of TextBlock, as shown 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 the current item of Collection within the DataContext.
For example, if we have to evaluate a specific property of the current item of CountriesList collection.
In this example, I want to show the value of property "EnglishName".
To do so, bind the Text Property of TextBlock, as shown 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 in detail. I hope this helps you to understand the concept of Binding and Expressions provided by WPF.