在XAML中绑定到Self/'this'

55

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

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

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

等等

谢谢

5个回答

92

首先在绑定中的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}"/> 

我有一个DataGrid,如果用户通过InputBinding的KeyBinding访问其内联ContextMenu的MenuItem的Command,并且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。 - Tom
答案是正确的,已经得到了赞同,因为它是有效的。但显然这是“XAML语法糖”的另一个例子... - mike

43

我认为你要找的是这个:

<Window x:Class = "blah blah all the regular stuff"

DataContext="{Binding RelativeSource={RelativeSource Self}}"

>

在 <Page> 主元素中,已验证仍可在 Windows 10 应用程序上运行。 - Lavamantis

30

我避免使用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
问题在于给XAML根元素命名(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别名实际上可以用于访问global::命名空间(尽管VS2013设计器似乎对此有所抱怨)。

结果:

enter image description here
一个其内容绑定到自身的窗口。


注意。


1

不幸的是,在UWP中,“ElementName = ...”似乎是唯一的命名根元素的方法,因为{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},因为它对我来说似乎更合乎逻辑和更安全。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接