Как использовать привязки WPF с RelativeSource?
Как я использую RelativeSource
с привязками WPF и каковы различные варианты использования?
15 ответов
Если вы хотите привязать к другому свойству объекта:
{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}
Binding RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...
Атрибут по умолчанию RelativeSource
это Mode
имущество. Полный набор допустимых значений приведен здесь ( из MSDN):
PreviousData Позволяет связать предыдущий элемент данных (не тот элемент управления, который содержит элемент данных) в списке отображаемых элементов данных.
TemplatedParent Указывает на элемент, к которому применяется шаблон (в котором существует элемент с привязкой к данным). Это похоже на установку TemplateBindingExtension и применимо только в том случае, если Binding находится внутри шаблона.
Self Относится к элементу, для которого вы устанавливаете привязку, и позволяет привязать одно свойство этого элемента к другому свойству того же элемента.
FindAncestor Относится к предку в родительской цепочке элемента с привязкой к данным. Вы можете использовать это для привязки к предку определенного типа или его подклассам. Этот режим вы используете, если хотите указать AncestorType и / или AncestorLevel.
Вот более наглядное объяснение в контексте архитектуры MVVM:
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}"/>
В этом случае мы не обязаны упоминать имя связывающего объекта, а ширина всегда будет равна высоте при каждом изменении высоты.
Если вы хотите, чтобы параметр Width равнялся половине высоты, вы можете сделать это, добавив конвертер в расширение разметки Binding. Давайте теперь представим другой случай:
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>
Приведенный выше случай используется для привязки данного свойства данного элемента к одному из его прямых родительских элементов, поскольку этот элемент содержит свойство, которое называется Parent. Это приводит нас к другому относительному режиму источника, который является FindAncestor.
- Режим 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.
- 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, который оценивается сразу после первого времени выполнения. Как вы можете заметить на рисунке ниже, фон и содержимое применяются из кнопки к шаблону элемента управления.
В 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}" />
д. 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
,
Представьте себе этот случай, прямоугольник, который мы хотим, чтобы его высота всегда была равна его ширине, скажем, квадрат. Мы можем сделать это, используя имя элемента
<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.
Я постоянно обновляю свои исследования Binding.
👉 Оригинал здесь
DataContext
DataContext - это DependencyProperty, включенный в FrameworkElement.
PresentationFramework.dll
namespace System.Windows
{
public class FrameworkElement : UIElement
{
public static readonly DependencyProperty DataContextProperty;
public object DataContext { get; set; }
}
}
И все элементы управления пользовательского интерфейса в WPF наследуют
FrameworkElement
класс.
На этом этапе изучения Binding или DataContext вам не нужно изучать FrameworkElement более глубоко.
Однако это краткое упоминание того факта, что ближайшим объектом, который может охватывать все элементы управления пользовательского интерфейса, является FrameworkElement.
DataContext всегда является ориентиром для привязки.
Привязка может напрямую вызывать значения для формата типа DataContext, начиная с ближайшего DataContext.
<TextBlock Text="{Binding}" DataContext="James"/>
Значение, привязанное к
Text="{Binding}"
передается непосредственно из ближайшего DataContext,
TextBlock
.
Следовательно, значение результата привязки
Text
это «Джеймс».
Тип целое число
1. Использование системы
При присвоении значения DataContext непосредственно из Xaml определения ресурсов в первую очередь требуются для таких типов значений, как Integer и Boolean. Поскольку все строки распознаются как String.mscrolib
в XamlТип переменной простого типа не поддерживается стандартом.
Вы можете определить это любым словом, но чаще всего используйтеsys
слова.xmlns:sys="clr-namespace:System;assembly=mscorlib"
YEAR
ключ ресурса в xamlОбъявите значение типа, который вы хотите создать, в форме StaticResource.
<Window.Resources> <sys:Int32 x:Key="YEAR">2020</sys:Int32> </Window.Resources> ... <TextBlock Text="{Binding}" DataContext="{StaticResource YEAR"/>
Все типы значений
Очень мало случаев, когда Value Type привязывается непосредственно к DataContext.
Потому что мы собираемся привязать объект.<Window.Resources> <sys:Boolean x:Key="IsEnabled">true</sys:Boolean> <sys:double x:Key="Price">7.77</sys:double> </Window.Resources> ... <StackPanel> <TextBlock Text="{Binding}" DataContext="{StaticResource IsEnabled}"/> <TextBlock Text="{Binding}" DataContext="{StaticResource Price}"/> </StackPanel>
Другой тип.
Возможны не только String, но и различные типы. Потому что DataContext - это объектный тип.
Ну наконец то...
При использовании привязки в WPF большинство разработчиков не полностью осознают существование, функцию и важность DataContext.
Это может означать, что привязка подключается по счастливой случайности.
Если вы отвечаете за большой проект WPF или участвуете в нем, вам следует более четко понимать иерархию DataContext приложения. Кроме того, введение в WPF различных популярных систем MVVM Framework без этой концепции DataContext создаст еще большие ограничения в свободной реализации функций.
Привязка
- Привязка DataContext
- Привязка элемента
- MultiBinding
- Самостоятельная привязка свойств
- Найти привязку предков
- TemplatedParent Binding
- Связывание статических свойств
Привязка DataContext
string property
<TextBox Text="{Binding Keywords}"/>
Привязка элемента
<CheckBox x:Name="usingEmail"/>
<TextBlock Text="{Binding ElementName=usingEmail, Path=IsChecked}"/>
MultiBinding
<TextBlock Margin="5,2" Text="This disappears as the control gets focus...">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}">
<Binding ElementName="txtUserEntry2" Path="Text.IsEmpty" />
<Binding ElementName="txtUserEntry2" Path="IsFocused" />
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
### Самостоятельная привязка свойств
<TextBlock x:Name="txt" Text="{Binding ElementName=txt, Path=Tag}"/>
Если вам нужно привязать собственное свойство, вы можете использовать
Self Property Binding
, Вместо того, чтобы использовать
Element Binding
.
Вам больше не нужно объявлять
x:Name
связывать собственное имущество.
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"/>
### Найдите импорт привязки предков на основе ближайшего к нему родительского элемента управления.
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Title}"/>
В дополнение к свойствам найденных элементов управления можно использовать свойства в объекте DataContext, если он существует.
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.Email}"/>
TemplatedParent Binding
Это метод, который можно использовать внутри, и вы можете импортировать элемент управления, который является владельцем
ControlTemplate
.
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
Вы можете получить доступ ко всем Property и DataContext.
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
Связывание статических свойств
Вы можете напрямую получить доступ к значению свойства привязки.
1. Объявитьstatic
имущество.
namespace Exam
{
public class ExamClass
{
public static string ExamText { get; set; }
}
}
2. Использование статического класса в XAML.
<Window ... xmlns:exam="clr-namespace:Exam">
3. Обязательное свойство.
<TextBlock Text="{Binding exam:ExamClass.ExamText}"/>
Или вы можете установить ключ ресурса, например, используя
Converter
.
<Window.Resource>
<cvt:VisibilityToBooleanConverter x:Key="VisibilityToBooleanConverter"/>
<exam:ExamClass x:Key="ExamClass">
</Window.Resource>
...
<TextBlock Text="{Binding Source={StaticResource ExamClass}, Path=ExamText}"/>
Я никогда не использовал статическое свойство при нормальных обстоятельствах. Это связано с тем, что данные, которые отклоняются от собственного DataContext, могут нарушить поток всех приложений WPF и значительно ухудшить удобочитаемость. Однако этот метод активно используется на этапе разработки для реализации быстрого тестирования и функций, а также в DataContext (или ViewModel).
Плохой переплет и хороший переплет
✔️ Если свойство, которое вы хотите привязать, включено в Datacontext,
вам не нужно использовать ElementBinding.
Использование ElementBinding через подключенный элемент управления не является функциональной проблемой,
но нарушает фундаментальный шаблон привязки.
<TextBox x:Name="text" Text="{Binding UserName}"/>
...
<TextBlock Text="{Binding ElementName=text, Path=Text}"/>
😀 Хорошая привязка
<TextBox Text="{Binding UserName}"/>
...
<TextBlock Text="{Binding UserName}"/>
✔️ Не используйте ElementBinding при использовании свойства, принадлежащего элементу управления более высокого уровня.
🙁 Плохой переплет <Window x:Name="win">
<TextBlock Text="{Binding ElementName=win, Path=DataContext.UserName}"/>
...
😀 Хорошая привязка
<Window>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.UserName}"/>
...
😆 Отлично!
<Window>
<TextBlock DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}"
Text="{Binding UserName}"/>
...
✔️ Не используйте ElementBinding при использовании собственных свойств.
🙁 Плохой переплет <TextBlock x:Name="txt" Text="{Binding ElementName=txt, Path=Foreground}"/>
😀 Хорошая привязка
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Foreground}"/>
Не забудьте TemplatedParent:
<Binding RelativeSource="{RelativeSource TemplatedParent}"/>
или же
{Binding RelativeSource={RelativeSource TemplatedParent}}
Я создал библиотеку, чтобы упростить синтаксис привязки 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. Без этого пример "ДО" был бы еще длиннее.
Стоит отметить, что для тех, кто наткнулся на это представление о Silverlight:
Silverlight предлагает только сокращенное подмножество этих команд
Некоторые полезные фрагменты:
Вот как это сделать в основном в коде:
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
Я только что опубликовал другое решение для доступа к DataContext родительского элемента в Silverlight, которое работает для меня. Оно использует Binding ElementName
,
Я не читал каждый ответ, но я просто хочу добавить эту информацию в случае относительного связывания исходной команды кнопки.
Когда вы используете относительный источник с Mode=FindAncestor
Привязка должна быть такой:
Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"
Если вы не добавите DataContext в свой путь, во время выполнения он не сможет получить свойство.
Это пример использования этого шаблона, который работал для меня на пустых сетках данных.
<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>
Если элемент не является частью визуального дерева, то 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}"/>
Но ни один из этих обходных путей, похоже, не работает, мы всегда получаем один и тот же результат...
На данный момент, кажется, что единственным жизнеспособным подходом было бы изменить видимость столбцов в коде, который мы обычно предпочитаем избегать при использовании шаблона MVVM... Но я не собираюсь сдаваться так скоро, по крайней мере, не пока есть другие варианты для рассмотрения
Решение нашей проблемы на самом деле довольно простое и использует преимущества класса Freezable. Основное назначение этого класса - определить объекты, которые имеют изменяемое и доступное только для чтения состояние, но в нашем случае интересной особенностью является то, что объекты Freezable могут наследовать DataContext, даже если они не находятся в визуальном или логическом дереве. Я не знаю точный механизм, который обеспечивает такое поведение, но мы собираемся воспользоваться этим, чтобы заставить нашу работу связывания...
Идея состоит в том, чтобы создать класс (я назвал его BindingProxy по причинам, которые должны стать очевидными очень скоро), который наследует Freezable и объявляет свойство зависимости Data:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Затем мы можем объявить экземпляр этого класса в ресурсах DataGrid и связать свойство Data с текущим DataContext:
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
Последний шаг - указать этот объект BindingProxy (легко доступный со StaticResource) в качестве источника для привязки:
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding Data.ShowPrice,
Converter={StaticResource visibilityConverter},
Source={StaticResource proxy}}"/>
Обратите внимание, что к пути привязки был добавлен префикс "Данные", поскольку теперь этот путь относится к объекту BindingProxy.
Привязка теперь работает правильно, и столбец правильно отображается или скрывается на основе свойства ShowPrice.