Cómo omitir la actualización de algunas de las sub-vinculaciones de un MultiBinding
? He definido en código subyacente (tuve algunos problemas al hacerlo en XAML y no creo que importe - después de que el código subyacente no sea menos expresivo que XAML) un MultiBinding
que toma dos propiedades de solo lectura y una propiedad normal para producir un solo valor En el caso de ConvertBack
, las propiedades de solo lectura no se modifican (mantienen su valor) y solo se cambia la propiedad normal.TwoWay MultiBinding con propiedades de solo lectura
Mientras que la definición de la MultiBinding
se fijó todo el MultiBinding
a TwoWay
sin embargo particulares sub-fijaciones donde conjunto adecuado (primero dos a OneWay
y la tercera dos TwoWay
).
El problema se produce bajo mi propio control. Sin embargo, en aras de la presentación, lo simplifiqué a un control más pequeño. El control presentado en este ejemplo es un control de tipo Slider
que permite seleccionar un valor en [0.0; 1.0] rango. El valor seleccionado está representado por el pulgar y expuesto como DependencyProperty
.
Básicamente el control se basa en una columna de 1 fila x 3 Grid
donde el pulgar está en la columna del medio. Para colocar correctamente la columna izquierda, se debe asignar un ancho correspondiente a la posición seleccionada. Sin embargo, este ancho depende también del ancho real de todo el control y del ancho real del pulgar (esto se debe a que la posición se da como un valor relativo en el rango [0.0; 1.0]).
Cuando se mueve el pulgar, la posición se debe actualizar de forma adecuada, sin embargo, el ancho del pulgar y el ancho del control obviamente no cambian.
El código funciona como se esperaba, sin embargo cuando se ejecuta en IDE durante el movimiento del pulgar La ventana de salida está llena de información de excepciones como se informó cuando MultiBinding
intenta establecer el valor de esas dos propiedades de solo lectura. Sospecho que no es dañino, sin embargo es algo molesto y engañoso. Y también significa que el código hace algo más que lo que quería que hiciera ya que no quería establecer esas propiedades (esto es importante en el caso de que no fueran de solo lectura y esto realmente las modificaría).
MultiBinding
documentation en la sección Observaciones menciona que las sub-consolidaciones individuales pueden anular el valor del modo MultiBinding
, pero parece que no funciona.
Tal vez esto podría resolverse de alguna manera expresando la dependencia del control y el ancho del pulgar (las propiedades de solo lectura) de alguna manera diferente. Por ejemplo, registrarse en sus notificaciones por separado y aplicar la actualización cuando se produzcan cambios. Sin embargo, no me parece natural. MultiBinding
, por otro lado, ya que, después de todo, el ancho de la columna izquierda depende de esas tres propiedades.
Aquí está el ejemplo de código XAML.
<UserControl x:Class="WpfTest.ExampleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="leftColumn" />
<ColumnDefinition x:Name="thumbColumn" Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Rectangle used in the left column for better visualization. -->
<Rectangle Grid.Column="0">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!-- Thumb representing the Position property. -->
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" />
<!-- Rectangle used in the right column for better visualization. -->
<Rectangle Grid.Column="2">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Black" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>
Y aquí es el código subyacente correspondiente
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfTest
{
public partial class ExampleUserControl : UserControl
{
#region PositionConverter
private class PositionConverter : IMultiValueConverter
{
public PositionConverter(ExampleUserControl owner)
{
this.owner = owner;
}
#region IMultiValueConverter Members
public object Convert(
object[] values,
Type targetType,
object parameter,
CultureInfo culture)
{
double thisActualWidth = (double)values[0];
double thumbActualWidth = (double)values[1];
double position = (double)values[2];
double availableWidth = thisActualWidth - thumbActualWidth;
double leftColumnWidth = availableWidth * position;
return new GridLength(leftColumnWidth);
}
public object[] ConvertBack(
object value,
Type[] targetTypes,
object parameter,
CultureInfo culture)
{
double thisActualWidth = owner.ActualWidth;
double thumbActualWidth = owner.thumbColumn.ActualWidth;
GridLength leftColumnWidth = (GridLength)value;
double availableWidth = thisActualWidth - thumbActualWidth;
double position;
if (availableWidth == 0.0)
position = 0.0;
else
position = leftColumnWidth.Value/availableWidth;
return new object[] {
thisActualWidth, thumbActualWidth, position
};
}
#endregion
private readonly ExampleUserControl owner;
}
#endregion
public ExampleUserControl()
{
InitializeComponent();
MultiBinding leftColumnWidthBinding = new MultiBinding()
{
Bindings =
{
new Binding()
{
Source = this,
Path = new PropertyPath("ActualWidth"),
Mode = BindingMode.OneWay
},
new Binding()
{
Source = thumbColumn,
Path = new PropertyPath("ActualWidth"),
Mode = BindingMode.OneWay
},
new Binding()
{
Source = this,
Path = new PropertyPath("Position"),
Mode = BindingMode.TwoWay
}
},
Mode = BindingMode.TwoWay,
Converter = new PositionConverter(this)
};
leftColumn.SetBinding(
ColumnDefinition.WidthProperty, leftColumnWidthBinding);
}
public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register(
"Position",
typeof(double),
typeof(ExampleUserControl),
new FrameworkPropertyMetadata(0.5)
);
public double Position
{
get
{
return (double)GetValue(PositionProperty);
}
set
{
SetValue(PositionProperty, value);
}
}
}
}
Gracias; esto es lo que necesitaba saber! ¿Hay alguna manera fácil de obtener los valores de entrada de esas propiedades en un ConvertBack? –
Gracias! Puede valer la pena mencionar también los enlaces "internos" individuales para los que desea que se vuelva a convertir el valor debe establecerse en 'Modo = TwoWay'. No encontré eso en la documentación. : / –