How do I use RelativeSource
with WPF bindings and what are the different use-cases?
If you want to bind to another property on the object:
{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
If you want to get a property on an ancestor:
{Binding Path=PathToProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
If you want to get a property on the templated parent (so you can do 2 way bindings in a ControlTemplate)
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
or, shorter (this only works for OneWay bindings):
{TemplateBinding Path=PathToProperty}
Binding RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...
The default attribute of RelativeSource
is the Mode
property. A complete set of valid values is given here (from MSDN):
PreviousData Allows you to bind the previous data item (not that control that contains the data item) in the list of data items being displayed.
TemplatedParent Refers to the element to which the template (in which the data-bound element exists) is applied. This is similar to setting a TemplateBindingExtension and is only applicable if the Binding is within a template.
Self Refers to the element on which you are setting the binding and allows you to bind one property of that element to another property on the same element.
FindAncestor Refers to the ancestor in the parent chain of the data-bound element. You can use this to bind to an ancestor of a specific type or its subclasses. This is the mode you use if you want to specify AncestorType and/or AncestorLevel.
Here's a more visual explanation in the context of a MVVM architecture:
https://i.stack.imgur.com/6Tcc6.jpg
{Binding Message}
(a bit more simple...)
Path=DataContext.Message
to get the binding to work. This makes sense, given that you can do relative bindings to width/height/etc. of a control.
Bechir Bejaoui exposes the use cases of the RelativeSources in WPF in his article here:
The RelativeSource is a markup extension that is used in particular binding cases when we try to bind a property of a given object to another property of the object itself, when we try to bind a property of a object to another one of its relative parents, when binding a dependency property value to a piece of XAML in case of custom control development and finally in case of using a differential of a series of a bound data. All of those situations are expressed as relative source modes. I will expose all of those cases one by one. Mode Self: Imagine this case, a rectangle that we want that its height is always equal to its width, a square let's say. We can do this using the element name
ListView
. The parent has 2 more ListView
levels below it. This helped me prevent passing data into each subsequent vm of each ListView
's DataTemplate
In WPF RelativeSource
binding exposes three properties
to set:
1. Mode: This is an enum
that could have four values:
a. PreviousData(value=0): It assigns the previous value of the property to the bound one b. TemplatedParent(value=1): This is used when defining the templates of any control and want to bind to a value/Property of the control. For example, define ControlTemplate:
<ControlTemplate>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
c. Self(value=2): When we want to bind from a self or a property of self. For example: Send checked state of checkbox as CommandParameter while setting the Command on CheckBox
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
d. FindAncestor(value=3): When want to bind from a parent control in Visual Tree. For example: Bind a checkbox in records if a grid,if header checkbox is checked
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />
2. AncestorType: when mode is FindAncestor
then define what type of ancestor
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}
3. AncestorLevel: when mode is FindAncestor
then what level of ancestor (if there are two same type of parent in visual tree
)
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}
Above are all use-cases for RelativeSource binding.
Don't forget TemplatedParent:
<Binding RelativeSource="{RelativeSource TemplatedParent}"/>
or
{Binding RelativeSource={RelativeSource TemplatedParent}}
It's worthy of note that for those stumbling across this thinking of Silverlight:
Silverlight offers a reduced subset only, of these commands
I created a library to simplify the binding syntax of WPF including making it easier to use RelativeSource. Here are some examples. Before:
{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
{Binding Path=Text, ElementName=MyTextBox}
After:
{BindTo PathToProperty}
{BindTo Ancestor.typeOfAncestor.PathToProperty}
{BindTo Template.PathToProperty}
{BindTo #MyTextBox.Text}
Here is an example of how method binding is simplified. Before:
// C# code
private ICommand _saveCommand;
public ICommand SaveCommand {
get {
if (_saveCommand == null) {
_saveCommand = new RelayCommand(x => this.SaveObject());
}
return _saveCommand;
}
}
private void SaveObject() {
// do something
}
// XAML
{Binding Path=SaveCommand}
After:
// C# code
private void SaveObject() {
// do something
}
// XAML
{BindTo SaveObject()}
You can find the library here: http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html
Note in the 'BEFORE' example that I use for method binding that code was already optimized by using RelayCommand
which last I checked is not a native part of WPF. Without that the 'BEFORE' example would have been even longer.
Some useful bits and pieces:
Here's how to do it mostly in code:
Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);
I largely copied this from Binding Relative Source in code Behind.
Also, the MSDN page is pretty good as far as examples go: RelativeSource Class
I just posted another solution for accessing the DataContext of a parent element in Silverlight that works for me. It uses Binding ElementName
.
This is an example of the use of this pattern that worked for me on empty datagrids.
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="We did't find any matching records for your search..." FontSize="16" FontWeight="SemiBold" Foreground="LightCoral"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
I didn't read every answer, but I just want to add this information in case of relative source command binding of a button.
When you use a relative source with Mode=FindAncestor
, the binding must be like:
Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"
If you don't add DataContext in your path, at execution time it can't retrieve the property.
I am constantly updating my research on Binding.
👉 Original Here
DataContext
DataContext is the DependencyProperty included in the FrameworkElement.
PresentationFramework.dll
namespace System.Windows
{
public class FrameworkElement : UIElement
{
public static readonly DependencyProperty DataContextProperty;
public object DataContext { get; set; }
}
}
And, all UI Controls in WPF inherit the FrameworkElement
class.
At this point in learning Binding or DataContext, you don't have to study FrameworkElement in greater depth. However, this is to briefly mention the fact that the closest object that can encompass all UI Controls is the FrameworkElement.
DataContext is always the reference point for Binding.
Binding can directly recall values for the DataContext type format starting with the nearest DataContext.
<TextBlock Text="{Binding}" DataContext="James"/>
The value bound to Text="{Binding}"
is passed directly from the nearest DataContext, TextBlock
.
Therefore, the Binding result value of Text
is 'James'.
Type integer When assigning a value to DataContext directly from Xaml, resource definitions are required first for value types such as Integer and Boolean. Because all strings are recognized as String. 1. Using System mscrolib in Xaml Simple type variable type is not supported by standard. You can define it with any word, but mostly use sys words. xmlns:sys="clr-namespace:System;assembly=mscorlib"
2. Create YEAR resource key in xaml Declare the value of the type you want to create in the form of a StaticResource.
All type of value There are very few cases where Value Type is binding directly into DataContext. Because we're going to bind an object.
Another type Not only String but also various types are possible. Because DataContext is an object type.
Finally...
In using Binding at WPF, most developers are not fully aware of the existence, function and importance of DataContext. It may mean that Binding is being connected by luck.
Especially if you are responsible for or participating in a large WPF project, you should understand the DataContext hierarchy of the application more clearly. In addition, the introduction of WPF's various popular MVVM Framework systems without this DataContext concept will create even greater limitations in implementing functions freely.
Binding
DataContext Binding
Element Binding
MultiBinding
Self Property Binding
Find Ancestor Binding
TemplatedParent Binding
Static Property Binding
DataContext Binding
string property
<TextBox Text="{Binding Keywords}"/>
Element Binding
<CheckBox x:Name="usingEmail"/>
<TextBlock Text="{Binding ElementName=usingEmail, Path=IsChecked}"/>
MultiBinding
<TextBlock Margin="5,2" Text="This disappears as the control gets focus...">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}">
<Binding ElementName="txtUserEntry2" Path="Text.IsEmpty" />
<Binding ElementName="txtUserEntry2" Path="IsFocused" />
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
<TextBlock x:Name="txt" Text="{Binding ElementName=txt, Path=Tag}"/>
If you have to bind your own property, you can use Self Property Binding
, instead of using Element Binding
.
You no longer have to declare x:Name
to bind your own property.
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Title}"/>
In addition to the properties of the controls found, the properties within the DataContext object can be used if it exists.
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.Email}"/>
TemplatedParent Binding
This is a method that can be used within ControlTemplate
, and you can import the control that is the owner of the ControlTemplate
.
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
You can access to all Property and DataContext.
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
Static Property Binding
You can access binding property value directly.
static
namespace Exam
{
public class ExamClass
{
public static string ExamText { get; set; }
}
}
<Window ... xmlns:exam="clr-namespace:Exam">
<TextBlock Text="{Binding exam:ExamClass.ExamText}"/>
Or, you can set Resource key like using Converter
.
<Window.Resource>
<cvt:VisibilityToBooleanConverter x:Key="VisibilityToBooleanConverter"/>
<exam:ExamClass x:Key="ExamClass">
</Window.Resource>
...
<TextBlock Text="{Binding Source={StaticResource ExamClass}, Path=ExamText}"/>
I have never used the Static Property under normal circumstances. This is because data that deviates from its own DataContext can disrupt the flow of whole WPF applications and impair readability significantly. However, this method is actively used in the development stage to implement fast testing and functions, as well as in the DataContext (or ViewModel).
Bad Binding & Good Binding
✔️ If the property you want to bind is included in Datacontext, you don't have to use ElementBinding.
Using ElementBinding through connected control is not a functional problem, but it breaks the fundamental pattern of Binding.
<TextBox x:Name="text" Text="{Binding UserName}"/>
...
<TextBlock Text="{Binding ElementName=text, Path=Text}"/>
<TextBox Text="{Binding UserName}"/>
...
<TextBlock Text="{Binding UserName}"/>
✔️ Do not use ElementBinding when using property belonging to higher layers control.
<Window x:Name="win">
<TextBlock Text="{Binding ElementName=win, Path=DataContext.UserName}"/>
...
<Window>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.UserName}"/>
...
<Window>
<TextBlock DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}"
Text="{Binding UserName}"/>
...
✔️ Do not use ElementBinding when using your own properties.
<TextBlock x:Name="txt" Text="{Binding ElementName=txt, Path=Foreground}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Foreground}"/>
If an element is not part of the visual tree, then RelativeSource will never work.
In this case, you need to try a different technique, pioneered by Thomas Levesque.
He has the solution on his blog under [WPF] How to bind to data when the DataContext is not inherited. And it works absolutely brilliantly!
In the unlikely event that his blog is down, Appendix A contains a mirror copy of his article.
Please do not comment here, please comment directly on his blog post.
Appendix A: Mirror of blog post
The DataContext property in WPF is extremely handy, because it is automatically inherited by all children of the element where you assign it; therefore you don’t need to set it again on each element you want to bind. However, in some cases the DataContext is not accessible: it happens for elements that are not part of the visual or logical tree. It can be very difficult then to bind a property on those elements…
Let’s illustrate with a simple example: we want to display a list of products in a DataGrid. In the grid, we want to be able to show or hide the Price column, based on the value of a ShowPrice property exposed by the ViewModel. The obvious approach is to bind the Visibility of the column to the ShowPrice property:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding ShowPrice,
Converter={StaticResource visibilityConverter}}"/>
Unfortunately, changing the value of ShowPrice has no effect, and the column is always visible… why? If we look at the Output window in Visual Studio, we notice the following line:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=ShowPrice; DataItem=null; target element is ‘DataGridTextColumn’ (HashCode=32685253); target property is ‘Visibility’ (type ‘Visibility’) The message is rather cryptic, but the meaning is actually quite simple: WPF doesn’t know which FrameworkElement to use to get the DataContext, because the column doesn’t belong to the visual or logical tree of the DataGrid.
We can try to tweak the binding to get the desired result, for instance by setting the RelativeSource to the DataGrid itself:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding DataContext.ShowPrice,
Converter={StaticResource visibilityConverter},
RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
Or we can add a CheckBox bound to ShowPrice, and try to bind the column visibility to the IsChecked property by specifying the element name:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding IsChecked,
Converter={StaticResource visibilityConverter},
ElementName=chkShowPrice}"/>
But none of these workarounds seems to work, we always get the same result…
At this point, it seems that the only viable approach would be to change the column visibility in code-behind, which we usually prefer to avoid when using the MVVM pattern… But I’m not going to give up so soon, at least not while there are other options to consider 😉
The solution to our problem is actually quite simple, and takes advantage of the Freezable class. The primary purpose of this class is to define objects that have a modifiable and a read-only state, but the interesting feature in our case is that Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree. I don’t know the exact mechanism that enables this behavior, but we’re going to take advantage of it to make our binding work…
The idea is to create a class (I called it BindingProxy for reasons that should become obvious very soon) that inherits Freezable and declares a Data dependency property:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
We can then declare an instance of this class in the resources of the DataGrid, and bind the Data property to the current DataContext:
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
The last step is to specify this BindingProxy object (easily accessible with StaticResource) as the Source for the binding:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding Data.ShowPrice,
Converter={StaticResource visibilityConverter},
Source={StaticResource proxy}}"/>
Note that the binding path has been prefixed with “Data”, since the path is now relative to the BindingProxy object.
The binding now works correctly, and the column is properly shown or hidden based on the ShowPrice property.
Success story sharing
AncestorType
.FindAncestor
, beforeAncestorType
, I get the following error: "RelativeSource is not in FindAncestor mode". (In VS2013, Community version){Binding Path=DataContext.SomeProperty, RelativeSource=...
. This was somewhat unexpected for me as a newbie when I was trying to bind to a parent's DataContext within a DataTemplate.