Связывание с Self/'this' в XAML
Простой вопрос WPF/XAML. В XAML, как я могу ссылаться на Self/this object в данном контексте? В очень базовом приложении с основным окном, одним элементом управления и закодированным свойством С# для окна я хочу привязать свойство элемента управления к свойству ручной кодировки окна.
В коде это очень просто - в конструкторе 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}"/>
и т.д.
Спасибо
Ответы
Ответ 1
Сначала используйте запятую между RelativeSource и Path в вашем привязке:
<Button x:Name="button1" Width="{Binding RelativeSource={RelativeSource Self},
Path=ButtonWidth}"/>
Во-вторых, RelativeSource привязывается к Button. Кнопка не имеет свойства ButtonWidth. Я предполагаю, что вам нужно связать свой родительский контроль.
Итак, попробуйте привязку RelativeSource:
<Button x:Name="button1" Width="{Binding RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type YourNamespace:YourParentControl}},
Path=ButtonWidth}"/>
Ответ 2
Я думаю, что вы ищете:
<Window x:Class = "blah blah all the regular stuff"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
Ответ 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
Проблема с именованием корневого элемента XAML
заключается в том, что, если вы привыкли использовать одно и то же имя (то есть "_this", "Root" и т.д.) Для всех корней в вашем проекте, тогда поздняя привязка во вложенном шаблоны могут получить доступ не к тому элементу. Это происходит потому, что, когда {Binding}
ElementName=...
используется в Template
, имена разрешаются во время выполнения, идя вверх по NameScope
дерева, пока первое не будет найдено совпадение.
Решение Clint избегает именования корневого элемента, но оно устанавливает корневой элемент в свой собственный 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::
пространству имен global::
(хотя дизайнер VS2013, похоже, жалуется на это).
Результат:
![enter image description here]()
Окно, содержимое которого связано с самим собой.
Н.Б.
Ответ 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}, так как он выглядит более логичным и безопасным для меня.