Как использовать привязки WPF с RelativeSource?
Как использовать RelativeSource
с привязками WPF и каковы различные варианты использования?
14 ответов
если вы хотите привязать к другому свойству объекта:
{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
если вы хотите получить свойство на предка:
{Binding Path=PathToProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
если вы хотите получить свойство на шаблонном родителе (так что вы можете сделать 2 способа привязки в ControlTemplate)
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
или, короче (это работает только для привязки oneway):
{TemplateBinding Path=PathToProperty}
Binding RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...
атрибут по умолчанию RelativeSource
это Mode
собственность. Полный набор допустимых значений приведен здесь (из MSDN):
PreviousData позволяет привязать предыдущий элемент данных (не тот элемент управления, который содержит элемент данных) в списке отображаемых элементов данных.
TemplatedParent относится к элементу, к которому относится шаблон (в котором привязан к данным элемент существует) применяется. Это похоже на установку TemplateBindingExtension и применимо только в том случае, если привязка находится в шаблоне.
Self относится к элементу, на котором вы устанавливаете привязку и позволяет привязать одно свойство этого элемента к другому свойству на том же элементе.
FindAncestor относится к предку в родительской цепочке элемента с привязкой к данным. Вы можете использовать это для привязка к предку определенного типа или его подклассам. Этот режим используется, если требуется указать AncestorType и / или AncestorLevel.
представьте себе этот случай, прямоугольник, который мы хотим, чтобы его высота всегда равна его ширине, скажем, квадрат. Мы можем сделать это, используя имя элемента
<Rectangle Fill="Red" Name="rectangle"
Height="100" Stroke="Black"
Canvas.Top="100" Canvas.Left="100"
Width="{Binding ElementName=rectangle,
Path=Height}"/>
но в данном случае мы обязаны указать имя объекта привязки, а именно прямоугольник. Мы можем достичь той же цели по-разному, используя RelativeSource
<Rectangle Fill="Red" Height="100"
Stroke="Black"
Width="{Binding RelativeSource={RelativeSource Self},
Path=Height}"/>
В этом случае мы не обязаны указывать имя объекта привязки, и ширина всегда будет равна Высота при изменении высоты.
Если вы хотите задать ширину как половину высоты, вы можете сделать это, добавив конвертер в расширение разметки привязки. Представим теперь другой случай:--4-->
<TextBlock Width="{Binding RelativeSource={RelativeSource Self},
Path=Parent.ActualWidth}"/>
приведенный выше случай используется для привязки данного свойства данного элемента к одному из его прямых родительских, поскольку этот элемент содержит свойство, которое называется родительским. Это приводит нас к другому относительному исходному режиму, который является FindAncestor.
Bechir Bejaoui предоставляет варианты использования RelativeSources в WPF в его статью здесь:
RelativeSource-это расширение разметки, которое используется, в частности случаи привязки, когда мы пытаемся привязать свойство данного объекта к другое свойство самого объекта, когда мы пытаемся связать свойство объекта к другому одному из его относительных родителей, при связывании зависимость стоимости недвижимости на кусок XAML в случае таможенного контроля разработка и, наконец, в случае использования дифференциала ряда связанные данные. Все эти ситуации выражаются как относительный источник режим. Я разоблачу все эти случаи один за другим.
- Self Режим:
представьте себе этот случай, прямоугольник, который мы хотим, чтобы его высота всегда равный его ширине, скажем, квадрат. Мы можем сделать это, используя имя элемента
<Rectangle Fill="Red" Name="rectangle" Height="100" Stroke="Black" Canvas.Top="100" Canvas.Left="100" Width="{Binding ElementName=rectangle, Path=Height}"/>
но в этом случае мы обязаны укажите название объект привязки, а именно прямоугольник. Мы можем достичь той же цели по-разному используя RelativeSource
<Rectangle Fill="Red" Height="100" Stroke="Black" Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>
в этом случае мы не обязаны упоминать имя привязки объект и ширина всегда будут равны высоте, когда высота меняется.
если вы хотите, чтобы параметр ширина была половиной высоты, то это можно сделать, добавив конвертер в расширение разметки привязки. Давайте теперь представьте себе другой случай:--8-->
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>
приведенный выше случай используется для привязки данного свойства данного элемента к один из его прямых родительских, поскольку этот элемент содержит свойство, которое звонил родитель. Это приводит нас к другому относительному исходному режиму, который в поиска одного.
- Режим FindAncestor
в этом случае свойство данного элемента будет привязано к одному из его родители Корса. Основное отличие от выше дело в том, что это до вас, чтобы определить тип предка и предка ранг в иерархии, чтобы связать свойство. Кстати, попробуй поиграть с этот кусок XAML
<Canvas Name="Parent0"> <Border Name="Parent1" Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}" Height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}"> <Canvas Name="Parent2"> <Border Name="Parent3" Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}" Height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}"> <Canvas Name="Parent4"> <TextBlock FontSize="16" Margin="5" Text="Display the name of the ancestor"/> <TextBlock FontSize="16" Margin="50" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}, AncestorLevel=2},Path=Name}" Width="200"/> </Canvas> </Border> </Canvas> </Border> </Canvas>
выше ситуация имеет два элемента TextBlock с тех встроены в пределах ряда границ и элементов canvas те представляют их иерархические родители. Второй TextBlock отобразит имя данный родитель на относительном уровне источника.
поэтому попробуйте изменить AncestorLevel=2 на AncestorLevel=1 и посмотрите, что происходит. Затем попробуйте изменить тип предка с AncestorType=граница с AncestorType=холст и посмотреть, что происходит.
отображаемый текст будет меняться в соответствии с типом предка и уровень. Тогда что произойдет, если уровень предка не подходит для тип предка? Это хороший вопрос, я знаю, что ты собираешься спросить его. Ответ: никаких исключений не будет и ничего не будет отображается на уровне TextBlock.
- TemplatedParent
этот режим позволяет привязать данное свойство ControlTemplate к свойству элемента управления, к которому применяется ControlTemplate. Хлынуть понять проблему вот пример ниже
<Window.Resources> <ControlTemplate x:Key="template"> <Canvas> <Canvas.RenderTransform> <RotateTransform Angle="20"/> </Canvas.RenderTransform> <Ellipse Height="100" Width="150" Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}"> </Ellipse> <ContentPresenter Margin="35" Content="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"/> </Canvas> </ControlTemplate> </Window.Resources> <Canvas Name="Parent0"> <Button Margin="50" Template="{StaticResource template}" Height="0" Canvas.Left="0" Canvas.Top="0" Width="0"> <TextBlock FontSize="22">Click me</TextBlock> </Button> </Canvas>
если я хочу применить свойства данного элемента управления к его элементу управления затем я могу использовать режим TemplatedParent. Существует также похожие на это расширение разметки, которое является TemplateBinding что является своего рода короткой рукой первого, но TemplateBinding вычисляется во время компиляции, в отличие от TemplatedParent, который оценивается сразу после первого времени выполнения. Как вы можете отметить на рисунке ниже, фон и содержание применяются изнутри кнопки к шаблону управления.
Не забудьте TemplatedParent:
<Binding RelativeSource="{RelativeSource TemplatedParent}"/>
или
{Binding RelativeSource={RelativeSource TemplatedParent}}
в WPF RelativeSource
привязка предоставляет три properties
в наборе:
1. Режим: это enum
что может иметь четыре значения:
a. PreviousData (
value=0
): он присваивает Предыдущее значениеproperty
в связанныйb. TemplatedParent (
value=1
): это используется при определенииtemplates
of любой управляйте и хотите привязать к значению / свойствуcontrol
.например, определение
ControlTemplate
:
<ControlTemplate>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
c. Себя (
value=2
): когда мы хотим связать сself
илиproperty
самостоятельно.например: отправить проверенное состояние
checkbox
asCommandParameter
при установкеCommand
onCheckBox
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
d. FindAncestor (
value=3
): когда хотите привязать от родителяcontrol
вVisual Tree
.например: связать
checkbox
наrecords
еслиgrid
,еслиheader
checkbox
проверен
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />
2. AncestorType: когда режим FindAncestor
затем определите, какой тип предка
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}
3. AncestorLevel: когда режим FindAncestor
тогда какой уровень предка (если есть два одинаковых типа родителя в visual tree
)
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}
выше все варианты использования для
RelativeSource binding
.
стоит отметить, что для тех, кто натыкается на это мышление Silverlight:
Silverlight предлагает только уменьшенное подмножество этих команд
Я создал библиотеку для упрощения синтаксиса привязки WPF, включая упрощение использования RelativeSource. Вот несколько примеров. Раньше:
{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
{Binding Path=Text, ElementName=MyTextBox}
после:
{BindTo PathToProperty}
{BindTo Ancestor.typeOfAncestor.PathToProperty}
{BindTo Template.PathToProperty}
{BindTo #MyTextBox.Text}
вот пример того, как упрощается привязка метода. Раньше:
// C# code
private ICommand _saveCommand;
public ICommand SaveCommand {
get {
if (_saveCommand == null) {
_saveCommand = new RelayCommand(x => this.SaveObject());
}
return _saveCommand;
}
}
private void SaveObject() {
// do something
}
// XAML
{Binding Path=SaveCommand}
после:
// C# code
private void SaveObject() {
// do something
}
// XAML
{BindTo SaveObject()}
вы можете найти библиотеку здесь:http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html
обратите внимание в Примере "до", что Я использую для привязки метода, что код уже оптимизирован с помощью RelayCommand
который последний раз я проверил, не является родной частью WPF. Без этого пример " До " был бы еще длиннее.
некоторые полезные биты и куски:
вот как это сделать в основном в коде:
Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);
Я в основном скопировал это из привязка относительного источника в коде за.
кроме того, страница MSDN довольно хороша, насколько примеры идут:Класс RelativeSource
Я только что опубликовал другое решение для доступа к DataContext родительского элемента в Silverlight, который работает для меня. Он использует Binding ElementName
.
Это пример использования этого шаблона, что работал для меня на пустой сетки данных.
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="We did't find any matching records for your search..." FontSize="16" FontWeight="SemiBold" Foreground="LightCoral"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
Я не читал каждый ответ, но я просто хочу добавить эту информацию в случае относительной привязки исходной команды кнопки.
когда вы используете относительный источник с Mode=FindAncestor
, привязка должна быть как:
Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"
Если вы не добавляете DataContext в свой путь, во время выполнения он не может получить свойство.
если элемент не является частью визуального дерева, RelativeSource никогда не будет работать.
в этом случае вам нужно попробовать другую технику, впервые предложенную Томасом Левеком.
у него есть решение в своем блоге под [WPF] как привязать к данным, когда DataContext не наследуется. И это работает абсолютно блестяще!
в маловероятном случае, если его блог не работает, приложение A содержит зеркальную копию его статья.
пожалуйста, не комментируйте здесь, пожалуйста комментарий непосредственно на его блоге.
приложение A: зеркало блога
свойство DataContext в WPF чрезвычайно удобно, поскольку оно автоматически наследуется всеми дочерними элементами элемента, которому вы его назначаете; поэтому вам не нужно устанавливать его снова для каждого элемента, который вы хотите привязать. Однако в некоторых случаях DataContext недоступен: это происходит для элементы, которые не являются частью зрительного или логического дерева. Тогда может быть очень сложно привязать свойство к этим элементам...
давайте проиллюстрируем простым примером: мы хотим отобразить список продуктов в DataGrid. В сетке мы хотим иметь возможность отображать или скрывать столбец цены на основе значения свойства ShowPrice, предоставляемого ViewModel. Очевидным подходом является привязка видимости столбца к ShowPrice свойство:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding ShowPrice,
Converter={StaticResource visibilityConverter}}"/>
к сожалению, изменение значения ShowPrice не имеет никакого эффекта, и столбец всегда виден... почему? Если мы посмотрим на окно вывода в Visual Studio, мы заметим следующую строку: