Как использовать привязки WPF с RelativeSource? - PullRequest
550 голосов
/ 17 сентября 2008

Как использовать RelativeSource с привязками WPF и каковы различные варианты использования?

Ответы [ 14 ]

734 голосов
/ 17 сентября 2008

Если вы хотите привязать к другому свойству объекта:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}

Если вы хотите получить собственность на предка:

{Binding Path=PathToProperty,
    RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}

Если вы хотите получить свойство для шаблонного родителя (то есть вы можете сделать двухсторонние привязки в ControlTemplate)

{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}

или, короче (это работает только для привязок OneWay):

{TemplateBinding Path=PathToProperty}
126 голосов
/ 03 марта 2009
Binding RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...

Атрибутом по умолчанию RelativeSource является свойство Mode. Полный набор допустимых значений приведен здесь ( из MSDN ):

  • PreviousData Позволяет связать предыдущий элемент данных (не тот элемент управления, который содержит элемент данных) в списке отображаемых элементов данных.

  • TemplatedParent Относится к элементу, к которому применяется шаблон (в котором существует элемент с привязкой к данным). Это похоже на установку TemplateBindingExtension и применимо только в том случае, если Binding находится внутри шаблона.

  • Self Относится к элементу, для которого вы устанавливаете привязку, и позволяет привязать одно свойство этого элемента к другому свойству того же элемента.

  • FindAncestor Указывает на предка в родительской цепочке элемента с привязкой к данным. Вы можете использовать это для привязки к предку определенного типа или его подклассам. Этот режим вы используете, если хотите указать AncestorType и / или AncestorLevel.

120 голосов
/ 16 марта 2011

Вот более наглядное объяснение в контексте архитектуры MVVM:

enter image description here

40 голосов
/ 09 ноября 2012

Представьте себе этот случай, прямоугольник, который мы хотим, чтобы его высота всегда была равна его ширине, скажем, квадрат. Мы можем сделать это, используя имя элемента

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

В этом случае мы не обязаны упоминать имя связывающего объекта, и ширина всегда будет равна высоте при каждом изменении высоты.

Если вы хотите, чтобы параметр Width был равен половине высоты, вы можете сделать это, добавив конвертер в расширение разметки Binding. Давайте теперь представим другой случай:

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
                   Path=Parent.ActualWidth}"/>

Приведенный выше случай используется для привязки данного свойства данного элемента к одному из его прямых родительских элементов, поскольку этот элемент содержит свойство, которое называется Parent. Это приводит нас к другому относительному режиму источника, который является FindAncestor.

34 голосов
/ 20 октября 2013

Bechir Bejaoui раскрывает случаи использования RelativeSources в WPF в его статье здесь :

RelativeSource - это расширение разметки, которое используется, в частности, обязательные случаи, когда мы пытаемся связать свойство данного объекта с другое свойство самого объекта, когда мы пытаемся связать свойство объекта к другому из его родственников, при связывании значение свойства зависимости для фрагмента XAML в случае пользовательского элемента управления разработка и, наконец, в случае использования дифференциала ряда связанные данные. Все эти ситуации выражены как относительный источник режимы. Я разоблачу все эти случаи один за другим.

  1. Режим 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}"/>

В этом случае мы не обязаны указывать название обязательного объект и ширина всегда будут равны высоте всякий раз, когда высота изменилась.

Если вы хотите, чтобы параметр Width был равен половине высоты, тогда Вы можете сделать это, добавив конвертер в расширение разметки Binding. Давайте теперь представим другой случай:

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
               Path=Parent.ActualWidth}"/>

Приведенный выше случай используется для привязки данного свойства данного элемента к один из его прямых родительских, так как этот элемент имеет свойство, которое называется родитель Это приводит нас к другому режиму относительного источника, который FindAncestor один.

  1. Режим 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 в пределах ряда границ и элементов холста, которые представляют их иерархические родители. Второй текстовый блок будет отображать имя данный родитель на относительном уровне источника.

Поэтому попробуйте изменить AncestorLevel = 2 на AncestorLevel = 1 и посмотрите, что случается. Затем попробуйте изменить тип предка от AncestorType = Border to AncestorType = Canvas и посмотрите, что происходит.

Отображаемый текст будет меняться в зависимости от типа предка и уровень. Тогда что произойдет, если уровень предка не подходит для тип предка? Это хороший вопрос, я знаю, что вы собираетесь спроси это. Ответ без исключений будет брошен и ничто не будет отображаться на уровне TextBlock.

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

23 голосов
/ 21 января 2016

В WPF RelativeSource привязка выставляет три properties для установки:

1. Режим: Это enum, который может иметь четыре значения:

а. PreviousData (value=0): Назначает предыдущее значение property связанный

б. TemplatedParent (value=1): Используется при определении templates из любой элемент управления и хотите привязать к значению / свойству control.

Например, определить ControlTemplate:

  <ControlTemplate>
        <CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
 </ControlTemplate>

с. Я (value=2): Когда мы хотим связать себя с self или property Я.

Например: Отправить проверенное состояние checkbox как CommandParameter при установке Command на CheckBox

<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: , когда mode равен 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.

Вот ссылка .

18 голосов
/ 17 сентября 2008

Не забудьте TemplatedParent:

<Binding RelativeSource="{RelativeSource TemplatedParent}"/>

или

{Binding RelativeSource={RelativeSource TemplatedParent}}
13 голосов
/ 06 августа 2012

Я создал библиотеку, чтобы упростить синтаксис привязки 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

Обратите внимание, что в примере 'BEFORE', который я использую для привязки метода, код уже был оптимизирован с помощью RelayCommand, который я в последний раз проверял, не является родной частью WPF. Без этого пример «ДО» был бы еще длиннее.

13 голосов
/ 24 апреля 2010

Стоит отметить, что для тех, кто сталкивается с этой мыслью о Silverlight:

Silverlight предлагает только сокращенное подмножество этих команд

12 голосов
/ 30 октября 2012

Некоторые полезные фрагменты:

Вот как это сделать в основном в коде:

Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);

Я в значительной степени скопировал это из Binding Relative Source в коде Behind .

Кроме того, страница MSDN довольно хороша в том, что касается примеров: RelativeSource Class

...