-
Notifications
You must be signed in to change notification settings - Fork 0
BindingProxy en
Attention! This article, as well as this announcement, are automatically translated from Russian.
A versatile resource that can be used in a variety of situations.
-
Value
- receives and returns any object, can be either a target property or a binding source.
Allocate a resource to the window's resources by associating it with some DataContext
property of the window. In the DataGrid
or ComboBox
resources, when marking up templates, if necessary, get a binding to this property without searching for the DataContext
of the window (since in templates DataContext
is the current element of the collection).
In MainWindow.xaml.cs
:
...
public RemoveCommand RemoveCommand { get; init; }
public ObservableCollection<DataHolder> Datas { get; init; } = new();
public bool IsDataEditable { get; set; }
...
public MainWindow()
{
DatasViewSource.Source = Datas;
RemoveCommand = new RemoveCommand(Data);
...
}
In the MainWindow.xaml
resource dictionary:
<Window.Resources>
<ResourceDictionary>
...
<l:BindingProxy x:Key="RemoveCommand" Value="{Binding RemoveCommand}"/>
<l:BindingProxy x:Key="IsEditable" Value="{Binding IsDatasEditable}"/>
...
</ResourceDictionary>
</Window.Resources>
In the DataGrid
template in 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>
Here we place the “Delete” button in each row, which is associated with the RemoveCommand
command, which is a property of the window, and as a parameter we pass a multi-binding to the current element of the collection and a window property indicating whether the collection is currently being edited.
Use as a cell to pass some current state or object to and from the markup. For example, in demo on the "Demo2" tab in the table, each field represents not only a value, but also the current data type. In order for the converter to know how to convert the string and field value when editing a cell, a window resource is introduced:
<Window.Resources>
<ResourceDictionary>
...
<l:BindingProxy x:Key="CurrentEditedItem"/>
...
</ResourceDictionary>
</Window.Resources>
In MainWindow.xaml.cs
, this resource is passed to the converter, which has a CurrentEditedItem
property for this:
public MainWindow()
{
...
InitializeComponent();
(FindResource("DataConverter") as DataConverter)!.CurrentEditedItem = FindResource("CurrentEditedItem") as BindingProxy;
}
When cell editing is enabled, the DataTemplateSelector
is called, which knows what what is currently being edited, and puts a reference to the CurrentEditedItem
resource. In 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;
}
returnEditValue;
}
return base.SelectTemplate(item, container);
}
This use case is not included in the demo, but it might be useful. If dependency injection is used, then the BindingProxy
resource can be passed to the IServiceProvider
markup.
In MainWindow.xaml
:
<Window.Resources>
<ResourceDictionary>
...
<l:BindingProxy x:Key="ServiceProvider"/>
...
</ResourceDictionary>
</Window.Resources>
In MainWindow.xaml.cs
:
public MainWindow(IServiceProvider services)
{
...
InitializeComponent();
(FindResource("ServiceProvider") as BindingProxy)!.Value = services;
}
Somewhere in the markup extension:
public override object ProvideValue(IServiceProvider serviceProvider)
{
IServiceProvider hostServiceProvider =((FindResource("ServiceProvider") as BindingProxy)!.Value as IServiceProvider)!;
....
}
Obviously, there is no extra IServiceProvider
here, since the argument of the ProvideValue
method does not provide those services that are configured before creating and starting the Host
.
This use case is not included in the demo, but it might be useful. Here BindingProxy
is used to pass a parameter or even an array of parameters to the converter:
In 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">
<!-- Parameter by which we select the processing method -->
<l:BindingProxy Value="SelectorParameter"/>
<!-- Parameter containing BoundProperty value -->
<StaticResourceResourceKey="BoundPropertyResourceKey"/>
<!-- Parameter with boolean flag, -->
<l:BindingProxy Name="BooleanFlag" Value="True" Type="{x:Type sys:Boolean}"/>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</TextBlock.Text>
</TextBlock>
In 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);
}
}
...
There are probably more options.
Before: (XamlServiceProviderCatcher) Start:(Overview) Next:(BindingProxyMarkup)