.net 如何将 WPF 绑定与 RelativeSource 结合使用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/84278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I use WPF bindings with RelativeSource?
提问by David Schmitt
How do I use RelativeSourcewith WPF bindings and what are the different use-cases?
如何使用RelativeSourceWPF 绑定以及有哪些不同的用例?
回答by Abe Heidebrecht
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)
如果您想在模板化父项上获取属性(因此您可以在 ControlTemplate 中进行两种方式的绑定)
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
or, shorter (this only works for OneWay bindings):
或者,更短(这只适用于 OneWay 绑定):
{TemplateBinding Path=PathToProperty}
回答by Drew Noakes
Binding RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...
The default attribute of RelativeSourceis the Modeproperty. A complete set of valid values is given here (from MSDN):
的默认属性RelativeSource是Mode属性。此处给出了一组完整的有效值(来自 MSDN):
PreviousDataAllows you to bind the previous data item (not that control that contains the data item) in the list of data items being displayed.
TemplatedParentRefers 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.
SelfRefers 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.
FindAncestorRefers 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.
PreviousData允许您绑定正在显示的数据项列表中的前一个数据项(不是包含该数据项的控件)。
TemplatedParent指应用模板(数据绑定元素存在于其中)的元素。这类似于设置 TemplateBindingExtension,并且仅适用于 Binding 在模板内的情况。
Self指的是您在其上设置绑定的元素,并允许您将该元素的一个属性绑定到同一元素上的另一个属性。
FindAncestor指数据绑定元素的父链中的祖先。您可以使用它来绑定到特定类型或其子类的祖先。如果您想指定 AncestorType 和/或 AncestorLevel,这就是您使用的模式。
回答by Jeffrey Knight
Here's a more visual explanation in the context of a MVVM architecture:
这是 MVVM 架构上下文中更直观的解释:


回答by Cornel Marian
Bechir Bejaoui exposes the use cases of the RelativeSources in WPF in his article here:
Bechir Bejaoui 在他的文章中公开了 WPF 中 RelativeSources 的用例:
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.
RelativeSource 是一种标记扩展,用于特定的绑定情况,当我们尝试将给定对象的属性绑定到对象本身的另一个属性时,当我们尝试将对象的属性绑定到它的另一个相关父级时,在自定义控件开发的情况下将依赖属性值绑定到一段 XAML 时,最后在使用一系列绑定数据的差异的情况下。所有这些情况都表示为相对源模式。我将一一揭露所有这些案例。
- 模式自我:
想象一下这种情况,我们想要一个矩形,它的高度总是等于它的宽度,比如说一个正方形。我们可以使用元素名称来做到这一点
<Rectangle Fill="Red" Name="rectangle" Height="100" Stroke="Black" Canvas.Top="100" Canvas.Left="100" Width="{Binding ElementName=rectangle, Path=Height}"/>但在上述情况下,我们必须指明绑定对象的名称,即矩形。我们可以使用 RelativeSource 以不同的方式达到相同的目的
<Rectangle Fill="Red" Height="100" Stroke="Black" Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>在这种情况下,我们不必提及绑定对象的名称,并且每当高度发生变化时,宽度将始终等于高度。
如果要将宽度参数设置为高度的一半,则可以通过向绑定标记扩展添加转换器来实现。现在让我们想象另一个案例:
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>上述情况用于将给定元素的给定属性与其直接父元素之一联系起来,因为该元素拥有一个名为 Parent 的属性。这将我们引向另一种相对源模式,即 FindAncestor 模式。
- 模式查找祖先
在这种情况下,给定元素的属性将与其父元素之一 Of Corse 相关联。与上述情况的主要区别在于,由您来确定层次结构中的祖先类型和祖先等级来绑定属性。顺便尝试一下这块 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>上面的情况是两个 TextBlock 元素,它们嵌入在一系列边框中,而 canvas 元素代表它们的分层父级。第二个 TextBlock 将在相对源级别显示给定父级的名称。
因此,尝试将 AncestorLevel=2 更改为 AncestorLevel=1,看看会发生什么。然后尝试将祖先的类型从 AncestorType=Border 更改为 AncestorType=Canvas,看看会发生什么。
显示的文本将根据祖先类型和级别而变化。那么如果祖先等级不适合祖先类型会发生什么?这是个好问题,我知道你会问这个问题。响应是不会抛出任何异常,并且不会在 TextBlock 级别显示任何内容。
- 模板化父级
此模式允许将给定的 ControlTemplate 属性与应用了 ControlTemplate 的控件的属性联系起来。为了很好地理解这里的问题,下面是一个例子
<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>如果我想将给定控件的属性应用于其控件模板,那么我可以使用 TemplatedParent 模式。还有一个与此标记扩展类似的扩展是 TemplateBinding,它是第一个的一种简写,但 TemplateBinding 在编译时评估,与 TemplatedParent 形成对比,TemplatedParent 在第一次运行后评估。如下图所示,背景和内容从按钮内应用到控件模板。
回答by Kylo Ren
In WPF RelativeSourcebinding exposes three propertiesto set:
在 WPFRelativeSource绑定中公开三个properties设置:
1. Mode:This is an enumthat could have four values:
1. 模式:这是一个enum可以有四个值的:
a. PreviousData(
value=0):It assigns the previous value of thepropertyto the bound oneb. TemplatedParent(
value=1):This is used when defining thetemplatesof any control and want to bind to a value/Property of thecontrol.For example,define
ControlTemplate:
一种。PreviousData(
value=0):将 的前一个值赋给property绑定的湾 TemplatedParent(
value=1):在定义templates任何控件的 并且想要绑定到 的值/属性时使用control。例如,定义
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 aselfor apropertyof self.For example:Send checked state of
checkboxasCommandParameterwhile setting theCommandonCheckBox
C。Self(
value=2):当我们想从一个self或一个propertyself绑定时。例如:在设置on时发送
checkboxas 的选中状态CommandParameterCommandCheckBox
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
d. FindAncestor(
value=3):When want to bind from a parentcontrolinVisual Tree.For example:Bind a
checkboxinrecordsif agrid,ifheadercheckboxis checked
d. FindAncestor(
value=3):如果想从父绑定control在Visual Tree。例如:Bind a
checkboxinrecordsif agrid,ifheadercheckbox被选中
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />
2. AncestorType:when mode is FindAncestorthen define what type of ancestor
2. AncestorType:当模式是FindAncestor然后定义什么类型的祖先
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}
3. AncestorLevel:when mode is FindAncestorthen what level of ancestor (if there are two same type of parent in visual tree)
3. AncestorLevel:当模式是FindAncestor什么级别的祖先(如果有两个相同类型的父级visual tree)
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}
Above are all use-cases for
RelativeSource binding.
以上是
RelativeSource binding.
回答by Bob King
Don't forget TemplatedParent:
不要忘记 TemplatedParent:
<Binding RelativeSource="{RelativeSource TemplatedParent}"/>
or
或者
{Binding RelativeSource={RelativeSource TemplatedParent}}
回答by Luis Perez
I created a library to simplify the binding syntax of WPF including making it easier to use RelativeSource. Here are some examples. Before:
我创建了一个库来简化 WPF 的绑定语法,包括使其更易于使用 RelativeSource。这里有些例子。前:
{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
您可以在此处找到该库:http: //www.simplygoodcode.com/2012/08/simple-wpf-binding.html
Note in the 'BEFORE' example that I use for method binding that code was already optimized by using RelayCommandwhich last I checked is not a native part of WPF. Without that the 'BEFORE' example would have been even longer.
请注意,在我用于方法绑定的“BEFORE”示例中,代码已经通过使用RelayCommand我最后检查的不是 WPF 的本机部分进行了优化。如果没有它,“BEFORE”示例会更长。
回答by Nathan Cooper
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.
我主要是从Binding Relative Source 的代码隐藏中复制的。
Also, the MSDN page is pretty good as far as examples go: RelativeSource Class
此外,就示例而言,MSDN 页面非常好:RelativeSource Class
回答by Matthew Black
It's worthy of note that for those stumbling across this thinking of Silverlight:
值得注意的是,对于那些对 Silverlight 有这种想法的人来说:
Silverlight offers a reduced subset only, of these commands
Silverlight 仅提供这些命令的缩减子集
回答by Juve
I just posted another solutionfor accessing the DataContext of a parent element in Silverlight that works for me. It uses Binding ElementName.
我刚刚发布了另一个解决方案,用于访问 Silverlight 中父元素的 DataContext 对我有用。它使用Binding ElementName.

