2009-11-19 29 views
6

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).

MultiBindingdocumentation 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); 
    } 
    } 

} 
} 

Respuesta

9

Finalmente encontré la solución yo mismo. En realidad está en el documentation - No sé cómo me perdí eso, pero pagué caro (en tiempo perdido) por ello.

Según la documentación ConvertBack debe volver Binding.DoNothing en posiciones en la que no hay ningún valor a ajustar (en particular hubo se desea la unión OneWay). Otro valor especial es DependencyProperty.UnsetValue.

Esto no es una solución completa ya que ahora la implementación IMultiValueConverter debe saber dónde devolver un valor especial. Sin embargo, creo que la mayoría de los casos razonables están cubiertos por esta solución.

+0

Gracias; esto es lo que necesitaba saber! ¿Hay alguna manera fácil de obtener los valores de entrada de esas propiedades en un ConvertBack? –

+1

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. : / –

4

Parece que MultiBinding no funciona bien. He visto un comportamiento inesperado (algo así como el tuyo) antes en mi práctica. También puede insertar puntos de interrupción o algún seguimiento en el convertidor y puede encontrar algunas cosas divertidas sobre qué convertidores y cuándo se llaman. Por lo tanto, si es posible, debe evitar usar MultiBinding. P.ej.puede agregar una propiedad especial en su modelo de vista que establecerá el valor de su propiedad mutable en su setter y devolverá el valor necesario usando las tres propiedades en su getter. Es algo así como un MultiValueConverter dentro de una propiedad =).

Espero que ayude.

+0

Si esto realmente es algún tipo de error en MultiBinding, entonces tal vez deberíamos informarlo de alguna manera. Donde hacer esto? –

+0

¿Intentó hacer lo mismo en .NET 4.0? Tal vez está arreglado ahora. (No puedo probar ahora debido a algunos problemas técnicos con mi VS 2010). – levanovd

Cuestiones relacionadas