DataContext
DataContext
is one of the most fundamental concepts in Data Binding. The Binding object needs to get its data from somewhere, and there are a few ways to specify the source of the data like using Source
property directly in the Binding, inheriting a DataContext
from the nearest element when traversing up in the tree, setting the ElementName
and RelativeSource
properties in the Binding
object.
User interface elements in WPF have a DataContext
dependency property. That property has the aforementioned "value inheritance" feature enabled, so if you set the DataContext
on an element to a Student
object, the DataContext
property on all of its logical descendant elements will reference that Student
object too. This means that all data bindings contained within that root element’s element tree will automatically bind against the Student
object, unless explicitly told to bind against something else. This feature is extremely useful while designing WPF applications. In the following example, we are setting the DataContext
in the code and all the elements in the window get to access the Student
object.
Exposing the Entire Object as DataContext from the Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DataContextSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Student objstudent = new Student();
objstudent.StudentName = "Kishore1021";
this.DataContext = objstudent;
}
public class Student
{
private string studentname;
public string StudentName
{
get
{
return studentname;
}
set
{
studentname = value;
}
}
}
}
}
XAML for the above code
<Window x:Class="DataContextSample.MainWindow"</li>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"> </RowDefinition>
</Grid.RowDefinitions>
<TextBox Name="TextBox1" Text="{Binding Path=StudentName}"></TextBox>
</Grid>
</Window>
- Download the source code from Microsoft here
Exposing Just a Single Property Value from the Code
In the Student
class, I added one more property Address
. In the MainWindow
, an Employee
object is created and initialized with some values and we are setting just the address as the datacontext
using this.DataContext = objstudent.Address;
in line28:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DataContextSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Student objstudent = new Student();
objstudent.StudentName = "Kishore1021";
objstudent.Address = "Washington DC";
this.DataContext = objstudent.Address;
}
public class Student
{
private string studentname;
private string address;
public string Address
{
get { return address; }
set { address = value; }
}
public string StudentName
{
get
{
return studentname;
}
set
{
studentname = value;
}
}
}
}
}
In the XAML code, I created two textblocks and assigned the address. Keep a watch on how I am using the DataContext
values. Since we are just setting the Address
as DataContext
, TextBlock1
binding doesn’t know where to find in that address
property. TextBlock2
binds to whatever is there in the datacontext
, which is just the address
property.
<Window x:Class="DataContextSample.MainWindow"
xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">
http://schemas.microsoft.com/winfx/2006/xaml</a>"
Title="MainWindow" Height="350" Width="525">
- Download the source code from here
To Understand the above concept better, see the following code. I have an employee
class and one more class called AnotherClass
and used employee
class as a property inside that class. In the mainwondow
class, we are creating an employee
class object and initializing it with some values. Then we are creating an anotherclass
object and initializing with the previously created employee
object as well as one more property of anotherclass
“State
” and then we are assigning this anotherclass
object as dataconext
. There are several ways to specify the binding source
object. Using the DataContext property on a parent element is useful when you are binding multiple properties to the same source. However, sometimes it may be more appropriate to specify the binding source on individual binding declarations.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace Bindingtoclasses
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Employee P = new Employee("Hello World");
P.State = "MD";
AnotherClass c = new AnotherClass();
c.EmployeeNameTest = P;
c.AnotherField = "Another Value";
this.DataContext = c;
}
}
public class AnotherClass : INotifyPropertyChanged
{
private string anotherfield;
private Employee emp;
public string AnotherField
{
get { return anotherfield; }
set
{
anotherfield = value;
OnPropertyChanged("AnotherField");
}
}
public Employee EmployeeNameTest
{
get { return emp; }
set
{
emp = value;
OnPropertyChanged("EmployeeNameTest");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public override string ToString()
{
return string.Format("My ToString implementation of AnotherClass");
}
}
public class Employee : INotifyPropertyChanged
{
private string name;
private string state;
public event PropertyChangedEventHandler PropertyChanged;
public Employee()
{
}
public Employee(string value)
{
this.name = value;
}
public string EmployeeName
{
get { return name; }
set
{
name = value;
OnPropertyChanged("EmployeeName");
}
}
public string State
{
get { return state; }
set
{
state = value;
OnPropertyChanged("State");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
In the XAML: Even though we assigned the entire anotherclass
object as datacontext
, I am just assigning the EmployeeNameTest
object in it as the datacontext
for the grid using the code <Grid DataContext="{Binding Path=EmployeeNameTest}">
.
<Window x:Class="Bindingtoclasses.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:Bindingtoclasses"Title="MainWindow" Height="350" Width="525">
<Grid DataContext="{Binding Path=EmployeeNameTest}">
<Grid.Resources>
<src:Employee x:Key="myDataSource" EmployeeName="Kishore1021"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBox Name="TextBox1" Grid.Row="0" Text="{Binding Source=
{StaticResource myDataSource}, Path=EmployeeName,
UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="TextBox2" Grid.Row="1" Text="{Binding Source=
{StaticResource myDataSource}, Path=EmployeeName}"></TextBox>
<TextBox Name="TextBox3" Grid.Row="2" Text="{Binding Path=EmployeeName,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBox Name="TextBox4" Grid.Row="3" Text="{Binding Path=EmployeeName}">
</TextBox>
<TextBlock Name="TextBlock1" Grid.Row="4" Text="{Binding Path=EmployeeName}"/>
<TextBlock Name="TextBlock2" Grid.Row="5" Text="{Binding Path=AnotherField}"/>
</Grid>
</Window>
- Download the source code from here
Priority of Source VS DataContext
Code Snippet
<Window x:Class="DataBinding_Source_Property.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:kishore="clr-namespace:DataBinding_Source_Property">
<Window.Resources>
<kishore:Person x:Key="PersonXAMLDataSource" FullName="Kishore1021"/>
<kishore:Person x:Key="Person1" FullName="Person1Name"/>
</Window.Resources>
<Grid DataContext="{StaticResource Person1}">
<Grid.RowDefinitions>
<RowDefinition Height="*"> </RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding Source=
{StaticResource PersonXAMLDataSource}, Path=FullName}"></TextBox>
<TextBox Grid.Row="1" Text="{Binding Path=FullName}"/>
</Grid>
</Window>
The first TextBox
inherits the DataContext
from the parent Grid
as well as has a Source
set in the Binding
object too. In this case, Source
takes priority, causing the TextBox
to bind to the Name
property of the resource with key “PersonXAMLDataSource
” – this displays “kishore1021
”. The second inherits the DataContext
from the parent Grid
causing the TextBox
to bind to the Name
property of the resource with key “Person1
”, causing it to display “Person1Name
”. WPF will search up the element tree until it encounters a DataContext
object if a Source
or RelativeSource
is not used. Once it finds a non-null
DataContext
, that object is used for binding. It is useful for binding several properties to the same object.
Most data bound applications tend to use DataContext
much more heavily than Source
. Use DataContext
only when you need to bind more than one property to a particular source. When binding only one property to a source, always use Source
. The reason for this is ease of debugging – We can see all the information about the Binding
in one place, than search for the nearest DataContext
to understand what is going on. Other than setting the DataContext
property on an element directly, inheriting the DataContext
value from an ancestor , and explicitly specifying the binding source by setting the Source
property on the Binding, you can also use the ElementName
property or the RelativeSource
property to specify the binding source. The ElementName
property is useful when you are binding to other elements in your application, such as when you are using a slider to adjust the width of a button. The RelativeSource
property is useful when the binding is specified in a ControlTemplate
or a Style
.
"We must be silent before we can listen. We must listen before we can learn. We must learn before we can prepare. We must prepare before we can serve. We must serve before we can lead." - William Arthur Ward