BindingProxy ru - Leksiqq/WpfMarkupExtension GitHub Wiki
Универсальный ресурс, который можно использовать в различных ситуациях.
-
Value
- получает и возвращает любой объект, может быть как целевым свойством, так и источником привязки. -
Name
- необязательное свойство типаString
, может использоваться для различения нескольких объектов, например, при передаче в качестве параметра в конвертер (смотри Пример 4). -
Type
- необязательное свойство типаType
, может использоваться для передачи информации об ожидаемом типе значения. Если установлено, тоValue
конвертируется в этот тип.
Разместить ресурс в ресурсах окна, связав его с каким-либо свойством DataContext
окна. В ресурсах DataGrid
или ComboBox
при разметке шаблонов при необходимости получить привязку к этому свойству, не занимаясь поиском DataContext
окна (так как в шаблонах DataContext
- это текущий элемент коллекции).
В MainWindow.xaml.cs
:
...
public RemoveCommand RemoveCommand { get; init; }
public ObservableCollection<DataHolder> Datas { get; init; } = new();
public bool IsDatasEditable { get; set; }
...
public MainWindow()
{
DatasViewSource.Source = Datas;
RemoveCommand = new RemoveCommand(Datas);
...
}
В словаре ресурсов MainWindow.xaml
:
<Window.Resources>
<ResourceDictionary>
...
<l:BindingProxy x:Key="RemoveCommand" Value="{Binding RemoveCommand}"/>
<l:BindingProxy x:Key="IsEditable" Value="{Binding IsDatasEditable}"/>
...
</ResourceDictionary>
</Window.Resources>
В шаблоне DataGrid
в MainWindow.xaml
:
<DataGrid ItemsSource="{Binding DatasViewSource.View}" AutoGenerateColumns="False" Margin="10,10,10,10"
CanUserAddRows="False" x:Name="DataGrid">
<DataGrid.Resources>
<DataTemplate x:Key="Actions">
<StackPanel Orientation="Horizontal">
<Button Content="➖" Command="{Binding Value, Source={StaticResource RemoveCommand}}"
ToolTip="Remove row">
<Button.CommandParameter>
<MultiBinding Converter="{l:ParameterizedResource DataConverter}"
ConverterParameter="RemoveCommandCanExecute">
<Binding Path="."/>
<Binding Path="Value" Source="{StaticResource IsEditable}"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
</StackPanel>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
...
<DataGridTemplateColumn Header="Actions" CellTemplate="{StaticResource Actions}"/>
</DataGrid.Columns>
</DataGrid>
Здесь мы в каждом ряду размещаем кнопку «Удалить», которая связана с командой RemoveCommand
, которая является свойством окна, а в качестве параметра передаём мультипривязку на текущий элемент коллекции и свойство окна, показывающее, является ли коллекция в данный момент редактируемой.
Использование в качестве ячейки для передачи какого-то текущего состояния или объекта в разметку и из неё. Например, в демо на вкладке «Demo2» в таблице каждое поле представляет собой не только значение, но и текущий тип данных. Чтобы конвертер мог узнать, как конвертировать строку и значение поля при редактировании ячейки, вводится ресурс окна:
<Window.Resources>
<ResourceDictionary>
...
<l:BindingProxy x:Key="CurrentEditedItem"/>
...
</ResourceDictionary>
</Window.Resources>
В MainWindow.xaml.cs
этот ресурс передаётся конвертеру, у которого для этого предусмотрено свойство CurrentEditedItem
:
public MainWindow()
{
...
InitializeComponent();
(FindResource("DataConverter") as DataConverter)!.CurrentEditedItem =
FindResource("CurrentEditedItem") as BindingProxy;
}
При включении редактирования ячейки вызывается DataTemplateSelector
, который знает, что в данный момент редактируется и помещает ссылку в ресурс CurrentEditedItem
. В TableDataTemplateSelector.cs
:
public string FieldName { get; set; } = null!;
...
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is DataHolder row
&& typeof(DataHolder).GetProperty(FieldName.ToString()!)?.GetValue(row) is FieldHolder field)
{
if(container is ContentPresenter cp && cp.FindResource("CurrentEditedItem") is BindingProxy bp)
{
bp.Value = field;
}
return EditValue;
}
return base.SelectTemplate(item, container);
}
Этот вариант использования в демо отсутствует, но может оказаться полезным. Если применяется внедрение зависимости, то через ресурс BindingProxy
можно передавать в разметку IServiceProvider
.
В MainWindow.xaml
:
<Window.Resources>
<ResourceDictionary>
...
<l:BindingProxy x:Key="ServiceProvider"/>
...
</ResourceDictionary>
</Window.Resources>
В MainWindow.xaml.cs
:
public MainWindow(IServiceProvider services)
{
...
InitializeComponent();
(FindResource("ServiceProvider") as BindingProxy)!.Value = services;
}
Где-то в расширении разметки:
public override object ProvideValue(IServiceProvider serviceProvider)
{
IServiceProvider hostServiceProvider =
((FindResource("ServiceProvider") as BindingProxy)!.Value as IServiceProvider)!;
....
}
Очевидно, лишнего IServiceProvider
здесь нет, так как аргумент метода ProvideValue
не предоставляет те сервисы, которые конфигурируются перед созданием и запуском Host
.
Этот вариант использования в демо отсутствует, но может оказаться полезным. Здесь BindingProxy
используется для передачи параметра или даже массива параметров в конвертер:
В MainWindow.xaml
:
...
<Window.Resources>
<ResourceDictionary>
...
<l:BindingProxy x:Key="BoundPropertyResourceKey" Name="BoundPropertyParameterName"
Value={Binding BoundProperty}/>
...
</ResourceDictionary>
</Window.Resources>
...
<TextBlock ...>
<TextBlock.Text>
<Binding Path="SomePath" Converter="{local:SomeConverter}">
<Binding.ConverterParameter>
<x:Array Type="l:BindingProxy">
<!-- Параметр, по которому выбираем способ обработки -->
<l:BindingProxy Value="SelectorParameter"/>
<!-- Параметр, содержащий значение BoundProperty -->
<StaticResource ResourceKey="BoundPropertyResourceKey"/>
<!-- Параметр с логическим флагом, -->
<l:BindingProxy Name="BooleanFlag" Value="True" Type="{x:Type sys:Boolean}"/>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBlock.Text>
</TextBlock>
В SomeConverter.cs
:
...
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(parameter is BindingProxy[] parameters && parameters.Length > 0)
{
switch(parameters[0].Value)
{
case "SelectorParameter":
if(parameters.Where(p => "BooleanFlag".Equals(p.Name)).Select(p => ).FirstValueOrDefault()
break;
....
}
Dictionary<string, object?> parameters = TakeParameters(parameter);
object selector = parameters[string.Empty]!;
return Convert(value, targetType, selector, parameters, culture);
}
}
...
Вероятно, есть ещё варианты.
Раньше: (XamlServiceProviderCatcher) Начало:(Обзор) Дальше:(BindingProxyMarkup)