Привязка к Self/this в XAML

Простой вопрос 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}"/> 

и т.д

Спасибо


person Tom Davies    schedule 01.10.2010    source источник


Ответы (5)


Сначала используйте запятую между RelativeSource и Path в привязке:

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

Во-вторых, RelativeSource привязывается к кнопке. Кнопка не имеет свойства с именем ButtonWidth. Я предполагаю, что вам нужно привязать к родительскому контролю.

Итак, попробуйте эту привязку RelativeSource:

<Button x:Name="button1" Width="{Binding RelativeSource=
    {RelativeSource FindAncestor, AncestorType={x:Type YourNamespace:YourParentControl}}, 
    Path=ButtonWidth}"/> 
person Arcturus    schedule 01.10.2010
comment
Большое спасибо за этот пост. Это мне очень помогло! Я искал хорошее решение уже 3 часа. - person Mr Tangjai; 17.03.2017
comment
У меня есть DataGrid, где, если пользователь получает доступ к одной из его встроенных команд MenuItem ContextMenu через KeyBinding InputBinding, чей CommandParameter = {Binding ElementName = MyDataGrid, Path = SelectedItems}, он передаст SelectedItems в Bound ICommand. Однако null передается, если к нему обращаются через ContextMenu. Я пробовал CommandParameter= {Binding SelectedItems}, {Binding ElementName=MyDataGrid, Path=SelectedItems}, {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItems}. Я установил CommandParameter до Command. - person Tom; 09.05.2017
comment
ответ правильный, проголосовал за, потому что он работает. но, видимо, это еще один пример синтаксического сахара xaml... - person mike; 08.07.2020

Я думаю, что вы ищете это:

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

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

>
person Clint StLaurent    schedule 07.11.2012
comment
Подтверждено по-прежнему работает в приложении Windows 10 в главном элементе «Страница». - person Lavamantis; 26.09.2016

Один из способов, с помощью которого мне приходится иметь дело с 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.

person Damian    schedule 29.11.2010

Проблема с именованием корневого элемента XAML заключается в том, что если вы привыкнете использовать одно и то же имя (например, " _this", "Root" и т. д.) для всех корней в вашем проекте, то поздняя привязка во вложенных шаблонах может получить доступ к неправильному элементу. Это связано с тем, что когда {Binding} ElementName=... используется в Template, имена разрешаются во время выполнения путем обхода дерева NameScope до тех пор, пока не будет найдено первое совпадение.

решение Клинта позволяет избежать именования корневого элемента, но устанавливает корневой элемент в свой собственный 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, кажется, жалуется на это).

Результат:

введите здесь описание изображения
Окно, содержимое которого связано само с собой.


Примечание

person Glenn Slayden    schedule 19.12.2014

К сожалению, именование корневого элемента с помощью «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}, так как мне это кажется более логичным и безопасным.

person cyanide    schedule 15.02.2017