【问题标题】:Binding to Self/'this' in XAML在 XAML 中绑定到 Self/'this'
【发布时间】:2010-10-01 09:01:11
【问题描述】:

简单的 WPF/XAML 问题。在 XAML 中,如何在给定的上下文中引用 Self/this 对象?在具有主窗口、一个控件和窗口的编码 C# 属性的非常基本的应用程序中,我想将控件的属性绑定到窗口的手动编码属性。

在代码中,这很容易——在 Window 的构造函数中,我添加了这个:

Binding bind = new Binding();
bind.Source = this;
bind.Path = new PropertyPath("ButtonWidth");
button1.SetBinding(WidthProperty, bind);

显然,我有一个名为 ButtonWidth 的属性和一个名为 button1 的控件。我无法弄清楚如何在 XAML 中执行此操作。像以下示例这样的各种尝试都没有奏效:

<Button x:Name="button1" Width="{Binding Source=Self Path=ButtonWidth}"/>

<Button x:Name="button1" Width="{Binding RelativeSource={RelativeSource Self} Path=ButtonWidth}"/> 

谢谢

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    首先在 Binding 中的 RelativeSource 和 Path 之间使用逗号:

    <Button x:Name="button1" Width="{Binding RelativeSource={RelativeSource Self}, 
                                    Path=ButtonWidth}"/> 
    

    其次,RelativeSource 绑定到 Button。 Button 没有名为 ButtonWidth 的属性。我猜你需要绑定到你的父控件。

    所以试试这个 RelativeSource 绑定:

    <Button x:Name="button1" Width="{Binding RelativeSource=
        {RelativeSource FindAncestor, AncestorType={x:Type YourNamespace:YourParentControl}}, 
        Path=ButtonWidth}"/> 
    

    【讨论】:

    • 非常感谢您的这篇文章。这对我帮助很大!我一直在寻找一个好的解决方案 3 小时了。
    • 我有一个 DataGrid,如果用户通过 InputBinding 的 KeyBinding 访问其内联 ContextMenu 的 MenuItem 的命令之一,其 CommandParameter="{Binding ElementName=MyDataGrid, Path=SelectedItems}",它将通过 SelectedItems绑定到 ICommand。但是,如果通过 ContextMenu 访问它,则会传递 null。我试过 CommandParameter="{Binding SelectedItems}"、"{Binding ElementName=MyDataGrid, Path=SelectedItems}"、"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItems}" .我确实在命令之前设置了 CommandParameter。
    • 答案是正确的,被赞成,因为它有效。但显然这是“xaml 语法糖”的另一个例子......
    【解决方案2】:

    我认为您正在寻找的是:

    <Window x:Class = "blah blah all the regular stuff"
    
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    
    >
    

    【讨论】:

    • 已验证在 主元素中的 Windows 10 应用程序上仍然有效。
    【解决方案3】:

    我不得不处理 RelativeSource 之类的一种方法是命名根 XAML 元素:

    <Window x:Class="TestApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        x:Name="_this"
        >
        <Grid>
            <Button x:Name="button" Width="{Binding ElementName=_this,Path=ButtonWidth}" />
        </Grid>
    </Window>
    

    如果你想设置 DataContext 你也可以这样做:

    <Window x:Class="TestApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        x:Name="_this"
        >
        <Grid DataContext="{Binding ElementName=_this}">        
            <Button x:Name="button" Width="{Binding Path=ButtonWidth}" />
        </Grid>
    </Window>
    

    我发现这是一个很好的技巧,不必记住 RelativeSource 绑定的所有复杂性。

    【讨论】:

      【解决方案4】:

      问题with naming the XAML root element 是,如果您习惯于对项目中的所有根使用相同的名称(即“_this”、“Root”等),那么嵌套中的后期绑定模板可能会访问错误的元素。这是因为,当在 Template 中使用 {Binding} ElementName=... 时,名称会在运行时通过向上遍历 NameScope 树直到找到第一个匹配项来解析。

      Clint's solution 避免命名根元素,但它将根元素设置为自己的DataContext,如果需要 DataContext 来处理数据,这可能不是一个选项。仅仅为了提供对元素的访问而在元素上引入另一个绑定似乎也有点笨拙。稍后,如果不再需要访问,{Binding} 将变得混乱:访问的责任属于目标和绑定。

      因此,这里有一个简单的标记扩展来访问 XAML 根元素而不用命名它:

      using System.Xaml;
      using System.Windows.Markup;
      
      public sealed class XamlRootExtension : MarkupExtension
      {
          public override Object ProvideValue(IServiceProvider sp)
          {
              var rop = sp.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
              return rop == null ? null : rop.RootObject;
          }
      };
      

      XAML

      <Window x:Class="MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:global="clr-namespace:">
      
          <TextBlock Text="{Binding Source={global:XamlRoot},Mode=OneTime}" />
      
      </Window>
      

      注意:为了清楚起见,我没有在命名空间中定义MarkupExtension;使用空的clr-namespace 别名,如下所示 d̲o̲e̲s̲ 实际上可以访问global:: 命名空间(尽管 VS2013 设计者似乎对此有所抱怨)。

      结果


      内容绑定到自身的窗口。


      注意

      【讨论】:

        【解决方案5】:

        不幸的是,使用“ElementName=..”命名根元素似乎是 UWP 的唯一方法,因为那里不支持 {RelativeSource Self}。

        奇怪的是,当名称在布局中被覆盖时,这仍然有效,例如

        <UserControl x:Class="Path.MyClass" x:Name="internalName">
           <Border Background={Binding Path=Background, ElementName=internalName}" ...
        

        然后

        <Page>
           <local:MyClass x:Name=externalName />
        
        </Page>
        

        顺便说一句,当同一布局中的不同元素使用相同的内部名称时,Windows 10 修复了一个错误(在 Windows 8.1 中存在)。

        不过,我更喜欢使用 {RelativeSource Self},因为它对我来说更合乎逻辑且更安全。

        【讨论】:

          猜你喜欢
          • 2011-02-10
          • 1970-01-01
          • 1970-01-01
          • 2017-02-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-13
          • 2013-09-13
          相关资源
          最近更新 更多