Как использовать привязки WPF с RelativeSource?

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

17.09.2008 15:10:53
13 ОТВЕТОВ
РЕШЕНИЕ

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

{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}
772
31.01.2017 09:26:46
Для этого «{Binding Path = PathToProperty, RelativeSource = {RelativeSource AncestorType = {x: Type typeOfAncestor}}}», похоже, ему нужно иметь «Mode = FindAncestor» перед «AncestorType»
EdwardM 2.02.2017 21:12:25
Для какой технологии? В WPF это выводится, когда вы указываете AncestorType.
Abe Heidebrecht 7.02.2017 21:13:29
Я согласен с @EdwardM. Когда я опускаю FindAncestorранее AncestorType, я получаю следующую ошибку: «RelativeSource не находится в режиме FindAncestor». (В VS2013, версия для сообщества)
kmote 10.04.2017 03:40:36
@kmote, это работает для меня с .net 3.0, и я еще раз убедился, что это работает в kaxaml ... Опять же, какую технологию вы используете? Процессор XAML отличается для WPF / Silverlight / UWP, поэтому вы можете получить разные результаты при использовании разных технологий. Вы также упомянули VS Community, так что, может быть, это предупреждение IDE, но оно работает во время выполнения?
Abe Heidebrecht 17.04.2017 20:01:53
Просто хотелось бы отметить , что если вы хотите привязать к свойству в DataContext в RelativeSource , то вы должны явно указать его: {Binding Path=DataContext.SomeProperty, RelativeSource=.... Это было несколько неожиданно для меня, как новичка, когда я пытался привязать к DataContext родителя в DataTemplate.
DrEsperanto 13.11.2018 19:04:45

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

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

или

{Binding RelativeSource={RelativeSource TemplatedParent}}
21
17.09.2008 15:14:34
Binding RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...

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

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

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

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

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

131
3.03.2009 09:24:23

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

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

16
24.04.2010 16:02:46
Да, я также искал поддержку SL. Проголосуйте: connect.microsoft.com/VisualStudio/feedback/details/480603/…
TravisWhidden 18.05.2010 18:31:52

Я только что опубликовал другое решение для доступа к DataContext родительского элемента в Silverlight, которое работает для меня. Это использует Binding ElementName.

12
23.05.2017 12:26:01

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

введите описание изображения здесь

125
5.02.2012 21:43:26
я что-то пропустил? Как вы можете считать это простой и понятной графикой? 1: поля слева означают, что они не связаны с правыми (почему внутри ViewModel есть файл .cs?) 2: на что указывают стрелки DataContext? 3: почему свойство Message отсутствует в ViewModel1? и самое главное 5. Зачем вам нужна привязка RelativeSource для доступа к DataContext окна, если TextBlock уже имеет этот же DataContext? Я явно что-то здесь упускаю, поэтому либо я довольно тупой, либо этот рисунок не так прост и понятен, как все думают! Пожалуйста, просветите меня
Markus Hütter 8.07.2016 18:09:38
@ MarkusHütter На диаграмме показана группа вложенных видов и соответствующих моделей представления. DataContext для View1 является ViewModel1, но он хочет привязать к свойству BaseViewModel. Поскольку BaseViewModel является DataContext для BaseView (который является окном), он может сделать это, найдя первый родительский контейнер, который является окном, и получит его DataContext.
mcargille 22.07.2016 14:37:31
@ MatthewCargille Я очень хорошо знаю, что это должно означать, это не моя точка зрения. Но поставьте себя на место того, кто плохо знает XAML и MVVM, и вы увидите, что это не просто и не ясно .
Markus Hütter 22.07.2016 18:36:26
Я должен согласиться с @ MarkusHütter, кстати, привязка слева может быть такой простой: {Binding Message}(немного более простой ...)
florien 17.08.2017 16:55:19
@florien Я так не думаю, по крайней мере, для моего случая использования. У меня есть DataTemplate, который должен ссылаться на DataContext MainWindow (мой класс viewmodel), чтобы получить список опций для выпадающего меню (загружается из базы данных). DataTemplate привязан к объекту модели, который также загружается из базы данных, но имеет доступ только к выбранной опции. Я должен был явно установить, Path=DataContext.Messageчтобы получить привязку к работе. Это имеет смысл, учитывая, что вы можете делать относительные привязки к ширине / высоте / и т.д. контроля.
DrEsperanto 13.11.2018 18:37:44

Я создал библиотеку, чтобы упростить синтаксис привязки 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. Без этого пример «ДО» был бы еще длиннее.

16
6.08.2012 18:12:51
Этот вид упражнений на руках демонстрирует слабость XAML; путь слишком сложен.
dudeNumber4 4.01.2017 14:37:48

Некоторые полезные кусочки:

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

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

16
19.05.2018 09:23:32
Моя расплывчатая память о WPF такова, что выполнение привязок в коде, вероятно, обычно не лучшая вещь.
Nathan Cooper 22.03.2017 19:58:42

Бехир Беджауи раскрывает случаи использования 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}"/>

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

  1. Режим FindAncestor

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

43
2.11.2013 16:51:29

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

<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>
9
29.04.2015 13:59:41

В 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>

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

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

<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />

д. FindAncestor ( value=3): когда хотите связать с родителемcontrol вVisual Tree.

Например: Bind a checkboxin recordsесли a 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.

Вот ссылка ссылка .

32
19.05.2018 09:24:28
Удивительно .. это сработало для меня: <DataGridCheckBoxColumn Header = "Платный" Ширина = "35" Binding = "{Binding RelativeSource = {RelativeSource Mode = FindAncestor, AncestorType = {x: Тип окна}}, Path = DataContext.SelectedBuyer.IsPaid , Mode = OneWay} "/> где я пытался привязать к свойству selectedbuyer родительского окна. Свойство IsPaid
Michael K 25.10.2018 00:21:26

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

Когда вы используете относительный источник с Mode=FindAncestor, привязка должна быть такой:

Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"

Если вы не добавите DataContext в свой путь, во время выполнения он не сможет получить свойство.

10
18.05.2018 10:46:55

Если элемент не является частью визуального дерева, то RelativeSource никогда не будет работать.

В этом случае вам нужно попробовать другую технику, впервые предложенную Томасом Левеском.

У него есть решение в его блоге в разделе [WPF] Как связывать данные, когда DataContext не наследуется . И это работает абсолютно блестяще!

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

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

Приложение A: Зеркало блога

Свойство DataContext в WPF чрезвычайно удобно, поскольку оно автоматически наследуется всеми дочерними элементами элемента, которому вы его назначаете; поэтому вам не нужно устанавливать его заново для каждого элемента, который вы хотите связать. Однако в некоторых случаях DataContext недоступен: это происходит для элементов, которые не являются частью визуального или логического дерева. Тогда может быть очень сложно связать свойство с этими элементами ...

Давайте проиллюстрируем это на простом примере: мы хотим отобразить список продуктов в DataGrid. В сетке мы хотим иметь возможность отображать или скрывать столбец Price, основываясь на значении свойства ShowPrice, предоставляемого ViewModel. Очевидный подход заключается в привязке видимости столбца к свойству ShowPrice:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding ShowPrice,
                Converter={StaticResource visibilityConverter}}"/>

К сожалению, изменение значения ShowPrice не имеет никакого эффекта, и столбец всегда виден ... почему? Если мы посмотрим на окно вывода в Visual Studio, мы увидим следующую строку:

Ошибка System.Windows.Data: 2: не удается найти управляющий FrameworkElement или FrameworkContentElement для целевого элемента. BindingExpression: Path = ShowPrice; DataItem = NULL; целевой элемент - «DataGridTextColumn» (HashCode = 32685253); Целевое свойство - «Видимость» (тип «Видимость»)

Сообщение довольно загадочное, но смысл на самом деле довольно прост: WPF не знает, какой FrameworkElement использовать для получения DataContext, поскольку столбец не принадлежит визуальному или логическому дереву DataGrid.

Мы можем попытаться настроить привязку, чтобы получить желаемый результат, например, установив RelativeSource на сам DataGrid:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding DataContext.ShowPrice,
                Converter={StaticResource visibilityConverter},
                RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>

Или мы можем добавить CheckBox, привязанный к ShowPrice, и попытаться связать видимость столбца со свойством IsChecked, указав имя элемента:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding IsChecked,
                Converter={StaticResource visibilityConverter},
                ElementName=chkShowPrice}"/>

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

На данный момент, кажется, что единственным жизнеспособным подходом было бы изменить видимость столбцов в code-behind, чего мы обычно предпочитаем избегать при использовании шаблона MVVM ... Но я не собираюсь сдаваться так скоро, по крайней мере, не в то время как есть другие варианты, чтобы рассмотреть

6
5.09.2017 10:46:12