如何与相对源使用WPF绑定?

时间:2022-04-16 15:02:01

How do I use RelativeSource with WPF bindings and what are the different use-cases?

我如何使用具有WPF绑定的相对源,以及哪些不同的用例?

14 个解决方案

#1


676  

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):

或者,更短(这只适用于单向绑定):

{TemplateBinding Path=PathToProperty}

#2


118  

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):

相对源的默认属性是模式属性。这里给出了一组完整的有效值(从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.

    PreviousData允许在显示的数据项列表中绑定之前的数据项(不是包含数据项的控件)。

  • 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.

    TemplatedParent是应用模板(在其中存在数据绑定元素)的元素。这类似于设置一个TemplateBindingExtension,只有当绑定在模板中时才适用。

  • 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.

    Self是指您正在设置绑定的元素,并允许您将该元素的一个属性绑定到同一元素上的另一个属性。

  • 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.

    FindAncestor是指数据绑定元素的父链中的祖先。您可以使用它绑定到特定类型或其子类的祖先。如果您想要指定AncestorType和/或AncestorLevel,这就是您使用的模式。

#3


111  

Here's a more visual explanation in the context of a MVVM architecture:

在MVVM体系结构的上下文中有一个更直观的解释:

如何与相对源使用WPF绑定?

#4


40  

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.

上面的例子用于将给定元素的给定属性绑定到它的直接父元素之一,因为该元素拥有一个称为父元素的属性。这就引出了另一个相对源模式,即FindAncestor 1。

#5


34  

Bechir Bejaoui exposes the use cases of the RelativeSources in WPF in his article here:

Bechir Bejaoui在他的文章中公开了WPF中相对资源的用例:

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.

RelativeSource标记扩展,用于特定的绑定情况下当我们试图将一个给定对象的属性绑定到另一个对象本身的属性,当我们试图将一个对象的属性绑定到另一个相对的父母,当绑定一个依赖项属性值一块XAML的自定义控件开发,最后使用微分的一系列的数据绑定。所有这些情况都表示为相对源模式。我将逐一展示这些案例。

  1. Mode Self:
  2. 模式:

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.

上面的例子用于将给定元素的给定属性绑定到它的直接父元素之一,因为该元素拥有一个称为父元素的属性。这就引出了另一个相对源模式,即FindAncestor 1。

  1. Mode FindAncestor
  2. 模式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

在这种情况下,给定元素的属性将被绑定到它的双亲之一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>

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.

上面的情况是两个TextBlock元素,这些元素嵌入在一系列的边框和画布元素中,这些元素代表了他们的分层父母。第二个TextBlock将在相对源级别显示给定父节点的名称。

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.

因此,尝试将AncestorLevel=2更改为AncestorLevel=1,看看会发生什么。然后尝试将祖先类型从AncestorType=Border改为AncestorType=Canvas,并查看发生了什么。

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.

显示的文本将根据祖先类型和级别进行更改。那么如果祖先层不适合祖先类型会发生什么呢?这是个好问题,我知道你会问的。响应不是抛出异常,nothings将显示在TextBlock级别。

  1. TemplatedParent
  2. 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

该模式支持将给定的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>

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.

如果我想将给定控件的属性应用到它的控制模板,那么我可以使用TemplatedParent模式。还有一个类似的标记扩展,即TemplateBinding,它是第一个的一种短指针,但是在编译时对TemplateBinding进行了评估,这是在第一次运行时计算的TemplatedParent的对比。正如您可以在bellow图中指出的那样,背景和内容从按钮中应用到控制模板。

#6


17  

Don't forget TemplatedParent:

不要忘记TemplatedParent:

<Binding RelativeSource="{RelativeSource TemplatedParent}"/>

or

{Binding RelativeSource={RelativeSource TemplatedParent}}

#7


13  

I created a library to simplify the binding syntax of WPF including making it easier to use RelativeSource. Here are some examples. Before:

我创建了一个库来简化WPF的绑定语法,包括使它更容易使用相对源。这里有一些例子。之前:

{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/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.

请注意,在“BEFORE”示例中,我使用的方法绑定代码已经通过使用RelayCommand进行了优化,最后我检查了它不是WPF的原生部分。如果没有这些,“以前”的例子将会更长。

#8


13  

In WPF RelativeSource binding exposes 3 properties to set:

在WPF相对源绑定中,公开了3个属性:

1. Mode : This is an enum that could have four values:

1。模式:这是一个可以有四个值的enum:

a. PreviousData(value=0) : It assigns the previous value of the property to the bound one

a. PreviousData(值=0):它将属性的前一个值赋给绑定的值。

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.

b. TemplatedParent(值=1):在定义任何控件的模板时使用,并希望绑定到控件的值/属性。

Ex. define ControlTemplate

例如定义往ControlTemplate中加故事板动画

  <ControlTemplate>
        <CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
 </ControlTemplate>

c. Self(value=2) : When want to bind from a self or a property of self.

c. Self(value=2):当想要从自我或自我的属性中绑定时。

Ex. Send checked state of checkbox as CommandParameter while setting the Command on CheckBox

在设置复选框的命令时,将checkbox的检查状态发送为CommandParameter。

<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />

d. FindAncestor(value=3) : When want to bind from a parent control in Visual Tree.

d. FindAncestor(值=3):当想要绑定到可视树中的父控件时。

Ex. 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

2。AncestorType:当模式为FindAncestor时,定义什么类型的祖先。

RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type iDP:XamDataGrid}}

3. AncestorLevel : when mode is FindAncestor then what level of ansector (if there are two same type of parent in visual tree)

3所示。AncestorLevel:当模式为FindAncestor时(如果在visualtree中有两种相同类型的父类),则是什么级别的an扇区

RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type iDP:XamDataGrid,AncestorLevel=1}}

Above are all use-cases for RelativeSource binding.

以上都是相对源绑定的用例。

Here is a ref link

这是一个ref链接。

#9


12  

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只提供了这些命令的简化子集。

#10


11  

Since this is top of Google for this problem, I thought I would 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: http://social.msdn.microsoft.com/Forums/en/wpf/thread/c5a59f07-c932-4715-8774-fa7e8472b75b

我在很大程度上抄袭了这个:http://social.msdn.microsoft.com/forums/en/wpf/thread/c5a59f07 -c932-4715- 8472b75b。

Also, the MSDN page is pretty good as far as examples go: http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx

同样,MSDN页面也很不错,比如:http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx。

#11


10  

I just posted another solution for accessing the DataContext of a parent element in Silverlight that works for me. It uses Binding ElementName.

我刚刚发布了另一个解决方案,用于访问Silverlight中为我工作的父元素的DataContext。它使用绑定ElementName。

#12


8  

This is an example of the use of this pattern that worked for me on empty datagrids.

这是在空的datagrid上为我工作的这个模式的一个例子。

<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>

#13


8  

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 relative source with Mode=FindAncestor, the binding must be like:

我没有读每个答案,但是我只是想在一个按钮的相对源命令绑定的情况下添加这个信息。当您使用模式=FindAncestor的相对源时,绑定必须是:

Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"

If you don't add DataContext in your path, at execution time he can't retrieve the property.

如果在路径中不添加DataContext,那么在执行时,他无法检索属性。

#14


3  

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.

在这种情况下,您需要尝试一种不同的技术,由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.

在他的博客不太可能发生的事件中,附录A包含了他的文章的镜像拷贝。

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…

WPF中的DataContext属性非常方便,因为它是由您指定的元素的所有子元素自动继承的;因此,您不需要在想要绑定的每个元素上重新设置它。然而,在某些情况下,DataContext是不可访问的:它发生在不属于视觉或逻辑树的元素中。在这些元素上绑定属性是非常困难的…

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:

让我们用一个简单的例子来说明:我们希望在DataGrid中显示产品列表。在网格中,我们希望能够根据ViewModel公开的显示价格属性的值显示或隐藏Price列。显而易见的方法是将列的可见性绑定到ShowPrice属性:

<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:

不幸的是,更改ShowPrice的值没有效果,而且列总是可见的…为什么?如果我们查看Visual Studio的输出窗口,我们会注意到以下一行:

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’)

System.Windows。数据错误:2:无法找到针对目标元素的管理框架元素或FrameworkContentElement。BindingExpression:路径=中的ShowPrice;DataItem =零;目标元素是“DataGridTextColumn”(HashCode=32685253);目标属性是“可见性”(类型“可视性”)

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.

消息是相当神秘的,但其含义实际上非常简单:WPF不知道要使用哪个FrameworkElement来获取DataContext,因为该列不属于DataGrid的可视化或逻辑树。

We can try to tweak the binding to get the desired result, for instance by setting the RelativeSource to the DataGrid itself:

我们可以尝试调整绑定以获得想要的结果,例如将相对源设置为DataGrid本身:

<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:

或者,我们可以添加一个绑定到ShowPrice的复选框,并通过指定元素名称将列可见性绑定到ischeck属性:

<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 ????

在这一点上,似乎唯一可行的方法是改变代码后面的列可视性,这是我们在使用MVVM模式时通常会避免的,但我不会这么快就放弃,至少在考虑其他选项的时候不会这么做。

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…

解决我们问题的方法实际上非常简单,并且利用了可*化的类。这个类的主要目的是定义具有可修改和只读状态的对象,但是在我们的案例中,有趣的特性是,可*化的对象可以继承DataContext,即使它们不在视觉或逻辑树中。我不知道这种行为的确切机制,但是我们会利用它来做我们的绑定工作…

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:

这个想法是创建一个类(我把它称为BindingProxy,因为它很快就会变得显而易见),它继承了Freezable并声明了一个数据依赖项属性:

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的资源中声明该类的实例,并将数据属性绑定到当前的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:

最后一步是指定这个BindingProxy对象(使用StaticResource容易访问)作为绑定的源:

<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.

请注意,绑定路径已经用“Data”前缀,因为路径现在相对于BindingProxy对象。

The binding now works correctly, and the column is properly shown or hidden based on the ShowPrice property.

绑定现在可以正常工作,并且根据ShowPrice属性正确显示或隐藏列。

#1


676  

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):

或者,更短(这只适用于单向绑定):

{TemplateBinding Path=PathToProperty}

#2


118  

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):

相对源的默认属性是模式属性。这里给出了一组完整的有效值(从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.

    PreviousData允许在显示的数据项列表中绑定之前的数据项(不是包含数据项的控件)。

  • 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.

    TemplatedParent是应用模板(在其中存在数据绑定元素)的元素。这类似于设置一个TemplateBindingExtension,只有当绑定在模板中时才适用。

  • 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.

    Self是指您正在设置绑定的元素,并允许您将该元素的一个属性绑定到同一元素上的另一个属性。

  • 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.

    FindAncestor是指数据绑定元素的父链中的祖先。您可以使用它绑定到特定类型或其子类的祖先。如果您想要指定AncestorType和/或AncestorLevel,这就是您使用的模式。

#3


111  

Here's a more visual explanation in the context of a MVVM architecture:

在MVVM体系结构的上下文中有一个更直观的解释:

如何与相对源使用WPF绑定?

#4


40  

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.

上面的例子用于将给定元素的给定属性绑定到它的直接父元素之一,因为该元素拥有一个称为父元素的属性。这就引出了另一个相对源模式,即FindAncestor 1。

#5


34  

Bechir Bejaoui exposes the use cases of the RelativeSources in WPF in his article here:

Bechir Bejaoui在他的文章中公开了WPF中相对资源的用例:

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.

RelativeSource标记扩展,用于特定的绑定情况下当我们试图将一个给定对象的属性绑定到另一个对象本身的属性,当我们试图将一个对象的属性绑定到另一个相对的父母,当绑定一个依赖项属性值一块XAML的自定义控件开发,最后使用微分的一系列的数据绑定。所有这些情况都表示为相对源模式。我将逐一展示这些案例。

  1. Mode Self:
  2. 模式:

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.

上面的例子用于将给定元素的给定属性绑定到它的直接父元素之一,因为该元素拥有一个称为父元素的属性。这就引出了另一个相对源模式,即FindAncestor 1。

  1. Mode FindAncestor
  2. 模式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

在这种情况下,给定元素的属性将被绑定到它的双亲之一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>

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.

上面的情况是两个TextBlock元素,这些元素嵌入在一系列的边框和画布元素中,这些元素代表了他们的分层父母。第二个TextBlock将在相对源级别显示给定父节点的名称。

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.

因此,尝试将AncestorLevel=2更改为AncestorLevel=1,看看会发生什么。然后尝试将祖先类型从AncestorType=Border改为AncestorType=Canvas,并查看发生了什么。

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.

显示的文本将根据祖先类型和级别进行更改。那么如果祖先层不适合祖先类型会发生什么呢?这是个好问题,我知道你会问的。响应不是抛出异常,nothings将显示在TextBlock级别。

  1. TemplatedParent
  2. 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

该模式支持将给定的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>

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.

如果我想将给定控件的属性应用到它的控制模板,那么我可以使用TemplatedParent模式。还有一个类似的标记扩展,即TemplateBinding,它是第一个的一种短指针,但是在编译时对TemplateBinding进行了评估,这是在第一次运行时计算的TemplatedParent的对比。正如您可以在bellow图中指出的那样,背景和内容从按钮中应用到控制模板。

#6


17  

Don't forget TemplatedParent:

不要忘记TemplatedParent:

<Binding RelativeSource="{RelativeSource TemplatedParent}"/>

or

{Binding RelativeSource={RelativeSource TemplatedParent}}

#7


13  

I created a library to simplify the binding syntax of WPF including making it easier to use RelativeSource. Here are some examples. Before:

我创建了一个库来简化WPF的绑定语法,包括使它更容易使用相对源。这里有一些例子。之前:

{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/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.

请注意,在“BEFORE”示例中,我使用的方法绑定代码已经通过使用RelayCommand进行了优化,最后我检查了它不是WPF的原生部分。如果没有这些,“以前”的例子将会更长。

#8


13  

In WPF RelativeSource binding exposes 3 properties to set:

在WPF相对源绑定中,公开了3个属性:

1. Mode : This is an enum that could have four values:

1。模式:这是一个可以有四个值的enum:

a. PreviousData(value=0) : It assigns the previous value of the property to the bound one

a. PreviousData(值=0):它将属性的前一个值赋给绑定的值。

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.

b. TemplatedParent(值=1):在定义任何控件的模板时使用,并希望绑定到控件的值/属性。

Ex. define ControlTemplate

例如定义往ControlTemplate中加故事板动画

  <ControlTemplate>
        <CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
 </ControlTemplate>

c. Self(value=2) : When want to bind from a self or a property of self.

c. Self(value=2):当想要从自我或自我的属性中绑定时。

Ex. Send checked state of checkbox as CommandParameter while setting the Command on CheckBox

在设置复选框的命令时,将checkbox的检查状态发送为CommandParameter。

<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />

d. FindAncestor(value=3) : When want to bind from a parent control in Visual Tree.

d. FindAncestor(值=3):当想要绑定到可视树中的父控件时。

Ex. 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

2。AncestorType:当模式为FindAncestor时,定义什么类型的祖先。

RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type iDP:XamDataGrid}}

3. AncestorLevel : when mode is FindAncestor then what level of ansector (if there are two same type of parent in visual tree)

3所示。AncestorLevel:当模式为FindAncestor时(如果在visualtree中有两种相同类型的父类),则是什么级别的an扇区

RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type iDP:XamDataGrid,AncestorLevel=1}}

Above are all use-cases for RelativeSource binding.

以上都是相对源绑定的用例。

Here is a ref link

这是一个ref链接。

#9


12  

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只提供了这些命令的简化子集。

#10


11  

Since this is top of Google for this problem, I thought I would 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: http://social.msdn.microsoft.com/Forums/en/wpf/thread/c5a59f07-c932-4715-8774-fa7e8472b75b

我在很大程度上抄袭了这个:http://social.msdn.microsoft.com/forums/en/wpf/thread/c5a59f07 -c932-4715- 8472b75b。

Also, the MSDN page is pretty good as far as examples go: http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx

同样,MSDN页面也很不错,比如:http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx。

#11


10  

I just posted another solution for accessing the DataContext of a parent element in Silverlight that works for me. It uses Binding ElementName.

我刚刚发布了另一个解决方案,用于访问Silverlight中为我工作的父元素的DataContext。它使用绑定ElementName。

#12


8  

This is an example of the use of this pattern that worked for me on empty datagrids.

这是在空的datagrid上为我工作的这个模式的一个例子。

<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>

#13


8  

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 relative source with Mode=FindAncestor, the binding must be like:

我没有读每个答案,但是我只是想在一个按钮的相对源命令绑定的情况下添加这个信息。当您使用模式=FindAncestor的相对源时,绑定必须是:

Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"

If you don't add DataContext in your path, at execution time he can't retrieve the property.

如果在路径中不添加DataContext,那么在执行时,他无法检索属性。

#14


3  

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.

在这种情况下,您需要尝试一种不同的技术,由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.

在他的博客不太可能发生的事件中,附录A包含了他的文章的镜像拷贝。

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…

WPF中的DataContext属性非常方便,因为它是由您指定的元素的所有子元素自动继承的;因此,您不需要在想要绑定的每个元素上重新设置它。然而,在某些情况下,DataContext是不可访问的:它发生在不属于视觉或逻辑树的元素中。在这些元素上绑定属性是非常困难的…

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:

让我们用一个简单的例子来说明:我们希望在DataGrid中显示产品列表。在网格中,我们希望能够根据ViewModel公开的显示价格属性的值显示或隐藏Price列。显而易见的方法是将列的可见性绑定到ShowPrice属性:

<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:

不幸的是,更改ShowPrice的值没有效果,而且列总是可见的…为什么?如果我们查看Visual Studio的输出窗口,我们会注意到以下一行:

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’)

System.Windows。数据错误:2:无法找到针对目标元素的管理框架元素或FrameworkContentElement。BindingExpression:路径=中的ShowPrice;DataItem =零;目标元素是“DataGridTextColumn”(HashCode=32685253);目标属性是“可见性”(类型“可视性”)

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.

消息是相当神秘的,但其含义实际上非常简单:WPF不知道要使用哪个FrameworkElement来获取DataContext,因为该列不属于DataGrid的可视化或逻辑树。

We can try to tweak the binding to get the desired result, for instance by setting the RelativeSource to the DataGrid itself:

我们可以尝试调整绑定以获得想要的结果,例如将相对源设置为DataGrid本身:

<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:

或者,我们可以添加一个绑定到ShowPrice的复选框,并通过指定元素名称将列可见性绑定到ischeck属性:

<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 ????

在这一点上,似乎唯一可行的方法是改变代码后面的列可视性,这是我们在使用MVVM模式时通常会避免的,但我不会这么快就放弃,至少在考虑其他选项的时候不会这么做。

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…

解决我们问题的方法实际上非常简单,并且利用了可*化的类。这个类的主要目的是定义具有可修改和只读状态的对象,但是在我们的案例中,有趣的特性是,可*化的对象可以继承DataContext,即使它们不在视觉或逻辑树中。我不知道这种行为的确切机制,但是我们会利用它来做我们的绑定工作…

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:

这个想法是创建一个类(我把它称为BindingProxy,因为它很快就会变得显而易见),它继承了Freezable并声明了一个数据依赖项属性:

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的资源中声明该类的实例,并将数据属性绑定到当前的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:

最后一步是指定这个BindingProxy对象(使用StaticResource容易访问)作为绑定的源:

<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.

请注意,绑定路径已经用“Data”前缀,因为路径现在相对于BindingProxy对象。

The binding now works correctly, and the column is properly shown or hidden based on the ShowPrice property.

绑定现在可以正常工作,并且根据ShowPrice属性正确显示或隐藏列。