2010-02-18 4 views
7

Estoy a punto de probar un control de usuario que he creado, y estoy encontrando algo que es inexplicable para mí.¿Por qué poner un Convertidor no operativo en un Enlace cambia su comportamiento?

El control es una extensión del ComboBox que maneja valores de un tipo personalizado específico. Tiene una propiedad de dependencia de ese tipo personalizado que es la propiedad de destino de un enlace.

Tengo una instrucción de rastreo en el colocador, y puedo ver que la propiedad se está estableciendo. Pero no está apareciendo en mi control de usuario.

Ahora, normalmente diría, está bien, tengo un error en mi control de usuario. Probablemente sí, aunque estoy desconcertado al respecto. Pero esta pregunta no se trata de encontrar el error bajo mi control. Sigue leyendo; aquí es donde se pone raro.

También estoy usando poca convertidor de valores de Bea Stollnitz para ayudar a depurar el Encuadernación:

public class DebuggingConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return value; // Add the breakpoint here!! 
    } 
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException("This method should never be called"); 
    } 
} 

La idea detrás de esto es que agrego este convertidor a mi Encuadernación y puede establecer un punto de interrupción para ver qué valor tiene siendo empujado hacia el objetivo. De acuerdo, eso funciona bien. Puedo ver que el valor está siendo expulsado.

De hecho, funciona un poco también bien. Si el DebuggingConverter está conectado a la vinculación, el control de usuario muestra el valor. Si no es así, no es así.

¿Cómo es eso posible? ¿Cómo podría un convertidor de valor que no afecta el comportamiento de un control vinculado?

Editar:

No es que sea probable que ayude, pero aquí está el XAML para el control de usuario:

<a:CodeLookupBox 
    Grid.Column="1" 
    Grid.IsSharedSizeScope="True" 
    MinWidth="100" 
    Style="{Binding Style}"> 
    <a:CodeLookupBox.CodeLookupTable> 
     <Binding Path="Codes" Mode="OneWay"/> 
    </a:CodeLookupBox.CodeLookupTable> 
    <a:CodeLookupBox.SelectedCode> 
     <Binding Path="Value" Mode="TwoWay" ValidatesOnDataErrors="True"/> 
    </a:CodeLookupBox.SelectedCode> 
</a:CodeLookupBox> 

Sin el convertidor en la segunda unión, el control se comporta como si yo no 't establece SelectedCode. Aunque una instrucción de seguimiento en el controlador OnSelectedCodePropertyChanged muestra que e.Value contiene efectivamente el valor correcto. Esto sucede independientemente de si el convertidor está conectado o no.

He estado tratando de aplicar este problema con un experimento mental: si quisiera crear un control de usuario vinculado cuyo comportamiento cambiara si un convertidor no-operativo estuviera unido a su enlace, ¿cómo lo haría? No sé lo suficiente sobre la vinculación para encontrar una respuesta.

+1

Esto es muy interesante. De cualquier forma, podrías pegar algo de XAML para mostrar los enlaces? –

+0

Esa es una buena pregunta. No tengo una respuesta, pero sé que la publicación en el sitio de Bea da otros consejos sobre la depuración de enlaces. ¿Hay alguna excepción lanzada cuando no está usando el convertidor? –

+0

Lo siento, no puedo ayudarte, pero +1 hace que DebuggingConverter sea una buena idea (aunque no es tu idea). –

Respuesta

2

Bueno, la buena noticia es que sé por qué SelectedCode no se está configurando cuando no estoy usando un convertidor de valor. La mala noticia es que todavía tengo algo de misterio, pero el problema se ha incrementado un poco en la cadena alimenticia, y tengo una solución alternativa.

Este control es esencialmente un cuadro combinado fuertemente tipado con un montón de características adicionales que son posibles por el hecho de que sabe qué tipo de elementos hay en él. Las propiedades SelectedCode y CodeLookupTable están fuertemente tipadas, y ocultan las propiedades subyacentes SelectedItem y ItemsSource, que no lo están. (Esto, dicho sea de paso, es la razón por la cual este es un control de usuario y no una subclase de ComboBox; no quiero que esas propiedades sean visibles porque pueden suceder muchas cosas si se configuran incorrectamente, ninguna de ellas es buena).

Esto es lo que está sucediendo. Este es mi salida de depuración cuando se conecta el convertidor de valores (el número es el código hash del control, porque tengo un montón de ellos que todos se ven inmersos simultáneamente cuando inicializado del programa):

14626603: OnCodeLookupTablePropertyChanged 
    CodeLookupTable property set to Proceedings.Model.CodeLookupTable 
    box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView 
14626603: OnSelectedCodePropertyChanged: 
    SelectedCode property set to Unlicensed Driver [VC12500(A)] 
    box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView 

Este es el comportamiento esperado La propiedad CodeLookupTable está configurada, por lo que establecer SelectedCode en uno de los elementos de esa colección establece correctamente SelectedItem en el ComboBox subyacente.

Pero sin el convertidor de valores, obtenemos lo siguiente:

16143157: OnSelectedCodePropertyChanged: 
    SelectedCode property set to Unlicensed Driver [VC12500(A)] 
    box.MainComboBox.ItemsSource = 
16143157: OnCodeLookupTablePropertyChanged 
    CodeLookupTable property set to Proceedings.Model.CodeLookupTable 
    box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView 

Aquí, la propiedad SelectedCode se está estableciendo antes de la propiedad es CodeLookupTable. Por lo tanto, cuando el método intenta establecer SelectedItem en el ComboBox subyacente, no ocurre nada, porque el ItemsSource es nulo.

Y aquí está la raíz del problema. Supuse tontamente que el orden en que los enlaces actualizan su objetivo es el mismo que el orden en el que se declararon en el XAML. (Una de las razones por las que he expresado los enlaces como elementos en lugar de atributos es porque el orden de los elementos en un documento XML es determinista y el orden de los atributos no. No es como si no hubiera pensado en esto). aparentemente no es el caso.

También asumí, quizás un poco menos tontamente, que el orden en el que los enlaces actualizan su destino no depende de si tienen conversores de valor adjuntos o no. Bueno, lo es. Me pregunto de qué más depende.

Afortunadamente, tengo una forma de evitar esto. Dado que mi objeto CodeLookup contiene una referencia al CodeLookupTable, puedo hacer que el setter SelectedCode establezca la propiedad CodeLookupTable (y por lo tanto ItemsSource) primero, si aún no se ha configurado. Eso hará que este problema desaparezca sin tener que pegar un convertidor de valor falso en el enlace y esperar que la forma en que se comportan los enlaces nunca cambie.

Editar

Esto es lo que las declaraciones de propiedades se ven como:

#region SelectedCode 

public static readonly DependencyProperty SelectedCodeProperty = DependencyProperty.Register(
    "SelectedCode", typeof(CodeLookup), typeof(CodeLookupBox), 
    new FrameworkPropertyMetadata(OnSelectedCodePropertyChanged)); 

private static void OnSelectedCodePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) 
{ 
    CodeLookupBox box = (CodeLookupBox)source; 
    CodeLookup code = e.NewValue as CodeLookup; 
    // this right here is the fix to the original problem: 
    if (box.CodeLookupTable == null && code != null) 
    { 
     box.CodeLookupTable = code.Table; 
    } 
    box.MainComboBox.SelectedItem = e.NewValue; 
} 

public CodeLookup SelectedCode 
{ 
    get { return GetValue(SelectedCodeProperty) as CodeLookup; } 
    set { SetValue(SelectedCodeProperty, value); } 
} 

#endregion 

#region CodeLookupTable 

public static readonly DependencyProperty CodeLookupTableProperty = DependencyProperty.Register(
    "CodeLookupTable", typeof(CodeLookupTable), typeof(CodeLookupBox), 
    new FrameworkPropertyMetadata(OnCodeLookupTablePropertyChanged)); 

private static void OnCodeLookupTablePropertyChanged(DependencyObject source, 
DependencyPropertyChangedEventArgs e) 
{ 
    CodeLookupBox box = (CodeLookupBox)source; 
    CodeLookupTable table = (CodeLookupTable)e.NewValue; 

    box.ViewSource = new CollectionViewSource { Source = table.Codes }; 
    box.View = box.ViewSource.View; 
    box.MainComboBox.ItemsSource = box.View; 

} 

public CodeLookupTable CodeLookupTable 
{ 
    get { return GetValue(CodeLookupTableProperty) as CodeLookupTable; } 
    set { SetValue(CodeLookupTableProperty, value); } 
} 

#endregion 
+0

Interesante. Supongo que 'SelectedCode' y' CodeLookup' son DPs. ¿Podría mostrar sus declaraciones y cómo pasa los cambios a 'ItemsSource' y' SelectedItem'? –

+0

Ellos son, y seguro; ver la edición de esta pregunta. –

0

intente implementar la función ConvertBack también, está utilizando enlace bidireccional, por lo que el problema puede ser que la excepción será ignorada por el "xaml", pero cuando ingrese en modo de depuración quizás pare el "enviar valor de nuevo "-¿operación?

+0

Una buena idea, tan bueno que ya lo hice. No cambió nada. Cambié ConvertBack para devolver 'value' y obtuve el mismo resultado. Además, no tengo que pasar por el depurador para que el convertidor tenga su efecto. Puedo ejecutar una versión de lanzamiento. –

0

Hmm ... muy extraño. He visto un comportamiento similar cuando se trata de MultiBindings con múltiples convertidores, pero no en un enlace simple. Por curiosidad, ¿cómo estás definiendo tus DP en el control?(Incluyendo las devoluciones de llamada, opciones de metadatos, etc.)

Algunas cosas para intentar (después de quitar el gancho convertidor):

  • defecto el modo (es decir, quitar el TwoWay)
  • Retire los ValidatesOnDataErrors
  • Agregue un AffectsRender al SelectedCode DP
0

¿Qué significa establecer SelectedCode? ¿Puedes publicar el código del manejador modificado de la propiedad?

Ha sugerido que la depuración muestre que la propiedad se está configurando con el valor correcto, por lo que la sugerencia más obvia es que el código que proporciona su comportamiento previsto es incorrecto.

Cuestiones relacionadas