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}
AncestorType
. –
Seyler FindAncestor
, before AncestorType
, I get the following error: "RelativeSource is not in FindAncestor mode". (In VS2013, Community version) –
Lobe {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. –
Fanaticize 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:
{Binding Message}
(a bit more simple...) –
Delirium 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. –
Fanaticize {Binding DataContext.DataContextProperty, ElementName=...}
works while {Binding DataContextProperty}
not. (I’m familiar with the use of the MVVM model and the concept of relative or data context bindings.) Window does not have such a property, indeed, had I assumed so? –
Delirium DataContext.YourProperty
, since the relative source just binds you to the ancestor node (ie. window, grid etc). –
Tyronetyrosinase 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
<Rectangle Fill="Red" Name="rectangle" Height="100" Stroke="Black" Canvas.Top="100" Canvas.Left="100" Width="{Binding ElementName=rectangle, Path=Height}"/>
But in this above case we are obliged to indicate the name of the binding object, namely the rectangle. We can reach the same purpose differently using the RelativeSource
<Rectangle Fill="Red" Height="100" Stroke="Black" Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>
For that case we are not obliged to mention the name of the binding object and the Width will be always equal to the Height whenever the height is changed.
If you want to parameter the Width to be the half of the height then you can do this by adding a converter to the Binding markup extension. Let's imagine another case now:
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>
The above case is used to tie a given property of a given element to one of its direct parent ones as this element holds a property that is called Parent. This leads us to another relative source mode which is the FindAncestor one.
- Mode FindAncestor
In this case, a property of a given element will be tied to one of its parents, Of Corse. The main difference with the above case is the fact that, it's up to you to determine the ancestor type and the ancestor rank in the hierarchy to tie the property. By the way try to play with this piece of XAML
<Canvas Name="Parent0"> <Border Name="Parent1" Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}" Height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}"> <Canvas Name="Parent2"> <Border Name="Parent3" Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}" Height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}"> <Canvas Name="Parent4"> <TextBlock FontSize="16" Margin="5" Text="Display the name of the ancestor"/> <TextBlock FontSize="16" Margin="50" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}, AncestorLevel=2},Path=Name}" Width="200"/> </Canvas> </Border> </Canvas> </Border> </Canvas>
The above situation is of two TextBlock elements those are embedded within a series of borders and canvas elements those represent their hierarchical parents. The second TextBlock will display the name of the given parent at the relative source level.
So try to change AncestorLevel=2 to AncestorLevel=1 and see what happens. Then try to change the type of the ancestor from AncestorType=Border to AncestorType=Canvas and see what's happens.
The displayed text will change according to the Ancestor type and level. Then what's happen if the ancestor level is not suitable to the ancestor type? This is a good question, I know that you're about to ask it. The response is no exceptions will be thrown and nothings will be displayed at the TextBlock level.
- TemplatedParent
This mode enables tie a given ControlTemplate property to a property of the control that the ControlTemplate is applied to. To well understand the issue here is an example bellow
<Window.Resources> <ControlTemplate x:Key="template"> <Canvas> <Canvas.RenderTransform> <RotateTransform Angle="20"/> </Canvas.RenderTransform> <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> <Canvas Name="Parent0"> <Button Margin="50" Template="{StaticResource template}" Height="0" Canvas.Left="0" Canvas.Top="0" Width="0"> <TextBlock FontSize="22">Click me</TextBlock> </Button> </Canvas>
If I want to apply the properties of a given control to its control template then I can use the TemplatedParent mode. There is also a similar one to this markup extension which is the TemplateBinding which is a kind of short hand of the first one, but the TemplateBinding is evaluated at compile time at the contrast of the TemplatedParent which is evaluated just after the first run time. As you can remark in the bellow figure, the background and the content are applied from within the button to the control template.
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
–
Seriate 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 theproperty
to the bound oneb. TemplatedParent(
value=1
): This is used when defining thetemplates
of any control and want to bind to a value/Property of thecontrol
.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 aself
or aproperty
of self.For example: Send checked state of
checkbox
asCommandParameter
while setting theCommand
onCheckBox
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
d. FindAncestor(
value=3
): When want to bind from a parentcontrol
inVisual Tree
.For example: Bind a
checkbox
inrecords
if agrid
,ifheader
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
.
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
1. Using System
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.mscrolib
in XamlSimple type variable type is not supported by standard.
You can define it with any word, but mostly usesys
words.
2. Createxmlns:sys="clr-namespace:System;assembly=mscorlib"
YEAR
resource key in xamlDeclare the value of the type you want to create in the form of a StaticResource.
<Window.Resources> <sys:Int32 x:Key="YEAR">2020</sys:Int32> </Window.Resources> ... <TextBlock Text="{Binding}" DataContext="{StaticResource YEAR"/>
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.<Window.Resources> <sys:Boolean x:Key="IsEnabled">true</sys:Boolean> <sys:double x:Key="Price">7.77</sys:double> </Window.Resources> ... <StackPanel> <TextBlock Text="{Binding}" DataContext="{StaticResource IsEnabled}"/> <TextBlock Text="{Binding}" DataContext="{StaticResource Price}"/> </StackPanel>
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>
### Self Property Binding
<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}"/>
### Find Ancestor Binding Imports based on the parent control closest to it.
<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.
1. Declarestatic
property.
namespace Exam
{
public class ExamClass
{
public static string ExamText { get; set; }
}
}
2. Using static class in XAML.
<Window ... xmlns:exam="clr-namespace:Exam">
3. Binding property.
<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}"/>
😀 Good Binding
<TextBox Text="{Binding UserName}"/>
...
<TextBlock Text="{Binding UserName}"/>
✔️ Do not use ElementBinding when using property belonging to higher layers control.
🙁 Bad Binding<Window x:Name="win">
<TextBlock Text="{Binding ElementName=win, Path=DataContext.UserName}"/>
...
😀 Good Binding
<Window>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.UserName}"/>
...
😆 Great!
<Window>
<TextBlock DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}"
Text="{Binding UserName}"/>
...
✔️ Do not use ElementBinding when using your own properties.
🙁 Bad Binding<TextBlock x:Name="txt" Text="{Binding ElementName=txt, Path=Foreground}"/>
😀 Good Binding
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Foreground}"/>
Don't forget TemplatedParent:
<Binding RelativeSource="{RelativeSource TemplatedParent}"/>
or
{Binding RelativeSource={RelativeSource TemplatedParent}}
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.
It's worthy of note that for those stumbling across this thinking of Silverlight:
Silverlight offers a reduced subset only, of these commands
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.
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.
© 2022 - 2024 — McMap. All rights reserved.