Привязка к себе / «это» в XAML - PullRequest
46 голосов
/ 01 октября 2010

Простой вопрос WPF / XAML. В XAML, как я могу ссылаться на Self / этот объект в данном контексте? В очень простом приложении с главным окном, одним элементом управления и закодированным свойством 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}"/> 

и т.д.

Спасибо

Ответы [ 5 ]

77 голосов
/ 01 октября 2010

Сначала используйте запятую между 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}"/> 
29 голосов
/ 07 ноября 2012

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

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

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

>
29 голосов
/ 30 ноября 2010

Один из способов справиться с 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 голосов
/ 20 декабря 2014

Проблема с присвоением имени корневому элементу 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>

note : для ясности я не определил MarkupExtension в пространстве имен; использование пустого псевдонима clr-namespace, как показано здесь d̲o̲e̲s̲ фактически работает для доступа к пространству имен global:: (хотя дизайнер VS2013, похоже, жалуется на это).

Результат

enter image description here
Окно, содержимое которого связано с самим собой.


нотабене

0 голосов
/ 15 февраля 2017

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...