如何将WPF绑定与RelativeSource一起使用?

时间:2008-09-17 15:10:54

标签: .net wpf xaml data-binding relativesource

如何将RelativeSource用于WPF绑定以及不同的用例?

15 个答案:

答案 0 :(得分:733)

如果要绑定到对象上的另一个属性:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}

如果你想在祖先上获得一个房产:

{Binding Path=PathToProperty,
    RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}

如果要在模板化父级上获取属性(这样可以在ControlTemplate中进行双向绑定)

{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}

或更短(这仅适用于OneWay绑定):

{TemplateBinding Path=PathToProperty}

答案 1 :(得分:126)

Binding RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...

RelativeSource的默认属性是Mode属性。这里给出了一整套有效值(from MSDN):

  • PreviousData 允许您在显示的数据项列表中绑定上一个数据项(而不是包含数据项的控件)。

  • TemplatedParent 指应用模板(其中存在数据绑定元素)的元素。这与设置TemplateBindingExtension类似,仅在Binding位于模板中时才适用。

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

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

答案 2 :(得分:120)

这是MVVM架构上下文中更直观的解释:

enter image description here

答案 3 :(得分:40)

想象一下这种情况,我们想要一个矩形,它的高度总是等于它的宽度,一个正方形让我们说。我们可以使用元素名称

来完成此操作
<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}"/>

对于这种情况,我们没有义务提及绑定对象的名称,并且每当高度改变时,宽度将始终等于高度。

如果要将宽度参数设置为高度的一半,则可以通过向Binding标记扩展添加转换器来执行此操作。 我们现在想象另一个案例:

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
                   Path=Parent.ActualWidth}"/>

上述情况用于将给定元素的给定属性绑定到其直接父元素之一,因为此元素包含一个名为Parent的属性。这导致我们进入另一个相对源模式,即FindAncestor模式。

答案 4 :(得分:34)

Bechir Bejaoui在his article here中公开了WPF中RelativeSources的用例:

  

RelativeSource是特别使用的标记扩展   我们尝试将给定对象的属性绑定到的绑定情况   当我们尝试绑定属性时,对象本身的另一个属性   一个对象的另一个相对父母,当绑定一个   在自定义控件的情况下,依赖属性值到一块XAML   发展,最后在使用一系列差异的情况下   绑定数据。所有这些情况都表示为相对来源   模式。我会逐一揭露所有这些案件。

     
      
  1. 模式自我:
  2.         

    想象一下这个案例,我们希望它的高度始终是一个矩形   等于它的宽度,一个正方形让我们说。我们可以使用   元素名称

    <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}"/>
    
         

    对于这种情况,我们没有义务提及绑定的名称   对象和宽度总是等于高度   高度改变了。

         

    如果要将宽度参数设置为高度的一半   您可以通过向Binding标记扩展添加转换器来完成此操作。   我们现在想象另一个案例:

     <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
                   Path=Parent.ActualWidth}"/>
    
         

    以上情况用于将给定元素的给定属性绑定到   它的一个直接父元素,因为这个元素拥有一个属性   叫做父母。这导致我们进入另一个相对源模式   FindAncestor一个。

         
        
    1. 模式FindAncestor
    2.         

      在这种情况下,给定元素的属性将绑定到其中一个元素   父母,科西嘉。与上述情况的主要区别在于事实   这取决于你确定祖先类型和祖先   在层次结构中排名以绑定属性。顺便试试玩   这片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元素   在一系列边框和画布元素中代表它们   等级父母。第二个TextBlock将显示名称   相对来源级别的父母。

           

      因此,尝试将AncestorLevel = 2更改为AncestorLevel = 1并查看内容   发生。然后尝试更改祖先的类型   AncestorType = AncestorType = Canvas的边框,看看发生了什么。

           

      显示的文字将根据Ancestor类型和更改而改变   水平。如果祖先等级不适合,那会发生什么   祖先类型?这是一个很好的问题,我知道你即将到来   问它。响应是没有异常将被抛出,并且没有例外   在TextBlock级别显示。

           
          
      1. TemplatedParent
      2.         

        此模式可以将给定的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在第一次运行时之后进行评估。如   你可以在下面的图中,背景和内容中加以评论   从按钮内应用到控件模板。

答案 5 :(得分:23)

在WPF RelativeSource绑定中,公开了三个properties来设置:

<强> 1。模式:这是enum,可以有四个值:

  

<强> 一个。 PreviousData(value=0): 它将property的先前值分配给   绑定的

     

<强> 湾TemplatedParent(value=1): 在定义templates时使用   任何控件,并希望绑定到control的值/属性。

     

例如,定义ControlTemplate

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

<强> ℃。自我(value=2): 当我们想要从selfproperty自我绑定时。

     

例如:checkbox

上设置CommandParameter时,将Command的已检查状态发送为CheckBox
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
  

<强> d。 FindAncestor(value=3): 当想要从父control绑定时   在Visual Tree

     

例如:如果选中checkbox records grid,则在header内绑定checkbox

<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />

<强> 2。 AncestorType:当模式为FindAncestor时,定义祖先的类型

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

<强> 3。 AncestorLevel: 当模式为FindAncestor时,祖先的级别(如果visual tree中有两种相同类型的父级)

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

以上是 RelativeSource binding 的所有用例。

<强> Here is a reference link

答案 6 :(得分:18)

不要忘记TemplatedParent:

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

{Binding RelativeSource={RelativeSource TemplatedParent}}

答案 7 :(得分:13)

值得注意的是,对于那些对Silverlight这个想法感到磕磕绊绊的人:

Silverlight仅提供这些命令的简化子集

答案 8 :(得分:13)

我创建了一个库来简化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}

后:

{BindTo PathToProperty}
{BindTo Ancestor.typeOfAncestor.PathToProperty}
{BindTo Template.PathToProperty}
{BindTo #MyTextBox.Text}

以下是如何简化方法绑定的示例。之前:

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

后:

// C# code
private void SaveObject() {
 // do something
}

// XAML
{BindTo SaveObject()}

您可以在此处找到图书馆:http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html

在'BEFORE'示例中注意我用于方法绑定的代码已经使用RelayCommand进行了优化,最后我检查的不是WPF的本机部分。没有它,'BEFORE'的例子会更长。

答案 9 :(得分:12)

一些有用的点点滴滴:

以下是如何在代码中执行此操作:

Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);

我在很大程度上是从 Binding Relative Source in code Behind 复制的。

此外,就示例而言,MSDN页面非常好: RelativeSource Class

答案 10 :(得分:10)

我刚发布了another solution,用于访问适用于我的Silverlight中父元素的DataContext。它使用Binding ElementName

答案 11 :(得分:9)

我没有阅读每个答案,但我只想在按钮的相对源命令绑定的情况下添加此信息。

当您使用Mode=FindAncestor的相对来源时,绑定必须如下:

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

如果您未在路径中添加DataContext,则在执行时无法检索该属性。

答案 12 :(得分:8)

这是在空数据网格上使用这种模式的一个例子。

<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 :(得分:4)

如果元素不是可视化树的一部分,那么RelativeSource将永远不会工作。

在这种情况下,你需要尝试一种由Thomas Levesque开创的不同技术。

他在[WPF] How to bind to data when the DataContext is not inherited下的博客上找到了解决方案。它的工作非常出色!

如果他的博客发生故障,附录A包含his article的镜像副本。

请不要在此发表评论,请comment directly on his blog post

附录A:博客文章的镜像

WPF中的DataContext属性非常方便,因为它会为您分配它的元素的所有子项自动继承;因此,您不需要在要绑定的每个元素上再次设置它。但是,在某些情况下,无法访问DataContext:它适用于不属于可视树或逻辑树的元素。那么在这些元素上绑定属性可能非常困难......

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

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding ShowPrice,
                Converter={StaticResource visibilityConverter}}"/>

不幸的是,更改ShowPrice的值没有任何效果,并且该列始终可见...为什么?如果我们在Visual Studio中查看“输出”窗口,我们会注意到以下行:

  

System.Windows.Data错误:2:找不到目标元素的管理FrameworkElement或FrameworkContentElement。 BindingExpression:路径= ShowPrice;的DataItem = NULL; target元素是'DataGridTextColumn'(HashCode = 32685253);目标属性是“可见性”(类型“可见性”)

     

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

我们可以尝试调整绑定以获得所需的结果,例如通过将RelativeSource设置为DataGrid本身:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding DataContext.ShowPrice,
                Converter={StaticResource visibilityConverter},
                RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>

或者我们可以添加一个绑定到ShowPrice的CheckBox,并尝试通过指定元素名称将列可见性绑定到IsChecked属性:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding IsChecked,
                Converter={StaticResource visibilityConverter},
                ElementName=chkShowPrice}"/>

但这些变通方法似乎都不起作用,我们总能得到相同的结果......

此时,似乎唯一可行的方法是改变代码隐藏中的列可见性,我们通常更喜欢在使用MVVM模式时避免...但我不会这么快就放弃,至少没有其他选择可以考虑

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

我们的想法是创建一个类(我称之为BindingProxy,其原因很快就会变得很明显),它继承了Freezable并声明了一个Data依赖属性:

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));
}

然后我们可以在DataGrid的资源中声明这个类的实例,并将Data属性绑定到当前的DataContext:

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

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

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding Data.ShowPrice,
                Converter={StaticResource visibilityConverter},
                Source={StaticResource proxy}}"/>

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

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

答案 14 :(得分:0)

我不断更新我对绑定的研究。

? 原版Here

数据上下文

DataContext 是包含在 FrameworkElement 中的 DependencyProperty。
PresentationFramework.dll

namespace System.Windows
{
    public class FrameworkElement : UIElement
    {
        public static readonly DependencyProperty DataContextProperty;
        public object DataContext { get; set; }
    }
}

而且,WPF 中的所有 UI 控件都继承 FrameworkElement 类。

<块引用>

此时在学习 Binding 或 DataContext 时,您不必更深入地学习 FrameworkElement。
然而,这只是简单地提到一个事实,可以包含所有 UI 控件的最接近的对象是 FrameworkElement。

DataContext 始终是绑定的参考点。

绑定可以直接调用从最近的 DataContext 开始的 DataContext 类型格式的值。

<TextBlock Text="{Binding}" DataContext="James"/>

绑定到 Text="{Binding}" 的值直接从最近的 DataContext TextBlock 传递。
因此,Text 的 Binding 结果值为 'James'。

  • 输入整数
    直接从 Xaml 为 DataContext 分配值时,首先需要为值类型(如整数和布尔值)定义资源。 因为所有的字符串都被识别为字符串。

    1. 在 Xaml 中使用系统 mscrolib <块引用>

    标准不支持简单类型变量类型。
    您可以使用任何词来定义它,但主要使用 sys 词。

    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    
    2.在xaml中创建YEAR资源键 <块引用>

    以静态资源的形式声明要创建的类型的值。

    <Window.Resources>
        <sys:Int32 x:Key="YEAR">2020</sys:Int32>
    </Window.Resources>
    ...
    <TextBlock Text="{Binding}" DataContext="{StaticResource YEAR"/>
    
  • 所有类型的价值
    值类型直接绑定到 DataContext 的情况很少。
    因为我们要绑定一个对象。

    <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>
    
  • 另一种类型
    不仅字符串而且各种类型都是可能的。因为 DataContext 是一种对象类型。

终于……

在 WPF 中使用 Binding 时,大多数开发人员并没有完全意识到 DataContext 的存在、功能和重要性。
这可能意味着 Binding 是靠运气连接的。

<块引用>

特别是如果你负责或参与大型 WPF 项目,你应该更清楚地了解应用程序的 DataContext 层次结构。此外,WPF 各种流行的 MVVM Framework 系统的引入没有这个 DataContext 概念,将在自由实现功能方面造成更大的限制。


绑定

  • 数据上下文绑定
  • 元素绑定
  • 多重绑定
  • 自属性绑定
  • 寻找祖先绑定
  • TemplatedParent 绑定
  • 静态属性绑定

数据上下文绑定

string property

<TextBox Text="{Binding Keywords}"/>

元素绑定

<CheckBox x:Name="usingEmail"/>
<TextBlock Text="{Binding ElementName=usingEmail, Path=IsChecked}"/>

多重绑定

<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}"/>

如果必须绑定自己的属性,可以使用Self Property Binding,而不是使用Element Binding
您不再需要声明 x:Name 来绑定您自己的属性。

<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"/>

### 查找祖先绑定 基于最接近它的父控件导入。
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Title}"/>

除了找到的控件的属性外,DataContext 对象中的属性如果存在也可以使用。

<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.Email}"/>

TemplatedParent 绑定

这是一个可以在ControlTemplate内使用的方法,可以导入属于ControlTemplate的拥有者的控件。

<Style TargetType="Button">
  <Setter Property="Template">
      <Setter.Value>
          <ControlTemplate TargetType="Button">
              <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
          </ControlTemplate>
      </Setter.Value>
  </Setter>

您可以访问所有 Property 和 DataContext。

<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>

静态属性绑定

您可以直接访问绑定属性值。

1. 声明 static 属性。
namespace Exam
{
  public class ExamClass
  {
      public static string ExamText { get; set; }
  }
} 
2. 在 XAML 中使用静态类。
<Window ... xmlns:exam="clr-namespace:Exam">
3. 绑定属性。
<TextBlock Text="{Binding exam:ExamClass.ExamText}"/>

或者,您可以像使用 Converter 一样设置资源键。

<Window.Resource>
  <cvt:VisibilityToBooleanConverter x:Key="VisibilityToBooleanConverter"/>
  <exam:ExamClass x:Key="ExamClass">
</Window.Resource>
...

<TextBlock Text="{Binding Source={StaticResource ExamClass}, Path=ExamText}"/>
<块引用>

我从来没有在正常情况下使用过静态属性。这是因为偏离其自身 DataContext 的数据可能会中断整个 WPF 应用程序的流程并显着降低可读性。但是,这种方法在开发阶段被积极使用,以实现快速测试和功能,以及在DataContext(或ViewModel)中。


坏绑定和好绑定

✔️ 如果要绑定的属性包含在 Datacontext 中,
您不必使用 ElementBinding。

通过连接控件使用 ElementBinding 不是功能问题,
但它打破了绑定的基本模式。

? 绑定不好
<TextBox x:Name="text" Text="{Binding UserName}"/>
...
<TextBlock Text="{Binding ElementName=text, Path=Text}"/>
? 绑定好
<TextBox Text="{Binding UserName}"/>
...
<TextBlock Text="{Binding UserName}"/>

✔️ 使用属于高层控件的属性时不要使用 ElementBinding。

? 绑定不好
<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}"/>
  ...

✔️ 使用自己的属性时不要使用 ElementBinding。

? 绑定不好
<TextBlock x:Name="txt" Text="{Binding ElementName=txt, Path=Foreground}"/>
? 绑定好
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Foreground}"/>