2011-02-02 11 views
31

OneWayToSource La unión parece roto en .NET 4.0OneWayToSource La unión parece roto en .NET 4.0

que tienen este simple trozo de Xaml

<StackPanel> 
    <TextBox Text="{Binding TextProperty, Mode=OneWayToSource}"/> 
    <Button/> 
</StackPanel> 

Y mi código detrás se ve así

public MainWindow() 
{ 
    InitializeComponent(); 
    this.DataContext = this; 
} 
private string m_textProperty; 
public string TextProperty 
{ 
    get 
    { 
     return "Should not be used in OneWayToSource Binding"; 
    } 
    set 
    { 
     m_textProperty = value; 
    } 
} 

En .NET 3.5 esto funciona como es posible excepto. Ponga un poco de texto en el TextBox, pulse la tecla Tab para hacer que perder el foco, y los TextProperty actualizaciones con cualquier texto que se ha introducido en el TextBox

En .NET 4.0, si escribo un texto en el TextBox y pulse Pestaña para hacer que pierda el Enfoque, el TextBox revierte al valor de TextProperty (es decir, "No se debe usar en el enlace OneWayToSource"). ¿Es esta relectura prevista para un enlace OneWayToSource en .NET 4.0? Solo quiero que el TextBox presione su valor en el TextProperty y no al revés.

actualización
Adición de una generosidad a esta pregunta, ya que se ha convertido en un inconveniente alcalde en mi proyecto y me gustaría saber la razón de que esto ha cambiado. Parece que se llama al get después de que el Enlace haya actualizado la fuente. ¿Es este el comportamiento deseado para un enlace OneWayToSource en .NET 4.0?

En caso afirmativo

  • ¿Cuál fue el problema con la forma en que trabajó en 3.5?
  • ¿En qué escenarios es mejor este nuevo comportamiento?

O está presente, de hecho, un error que podemos tener la esperanza de ser arreglados en una versión futura?

Respuesta

9

el blog de Karl Shifflett y la respuesta de @ Simpzon ya cubren por qué se añaden con esta función y por qué no es un problema para las propiedades que conseguir siempre lo que se estableció. En su propio código, siempre utiliza una propiedad intermedia que tiene la semántica adecuada para el enlace y utiliza una propiedad interna que tiene la semántica que desea. Llamaría a la propiedad intermedia una propiedad de "bloqueo" porque bloquea el getter para que no llegue a su propiedad interna.

Pero en el caso en que no tenga acceso al código fuente de la entidad en la que está configurando la propiedad y desea el comportamiento anterior, puede usar un convertidor.

Aquí es un convertidor de bloqueo con el estado:

public class BlockingConverter : IValueConverter 
{ 
    public object lastValue; 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     return lastValue; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     lastValue = value; 
     return value; 
    } 
} 

y se puede utilizar para su ejemplo como este:

<Grid> 
    <Grid.Resources> 
     <local:BlockingConverter x:Key="blockingConverter" x:Shared="False"/> 
    </Grid.Resources> 
    <StackPanel> 
     <TextBox Text="{Binding TextProperty, Mode=OneWayToSource, Converter={StaticResource blockingConverter}}"/> 
     <Button Content="Click"/> 
    </StackPanel> 
</Grid> 

Tenga en cuenta que debido a que el convertidor tiene un estado que necesita una instancia independiente cada vez que se usa el recurso y para esto podemos usar el atributo x:Shared="False" en el recurso.

+4

Gracias por su respuesta y el convertidor pero no estoy de acuerdo. Su respuesta y el blog de Karl Shifflett explican por qué esta característica se agregó para cada modo, excepto 'OneWayToSource'. Digamos que hago alguna conversión, formato o lo que sea en el setter, aún así no quiero que mi 'TextBox' se actualice a ese valor. No solo rompieron la forma en que funcionaba antes (de lo que nunca escuché a nadie quejarse), ahora también es un error porque ya no es 'OneWayToSource'. Está en un punto intermedio como 'TwoWay' sin actualizar la notificación de cambio –

+1

No mencioné que el nuevo comportamiento de' OneWayToSource' me resulta sorprendente e inútil. No quise parecer justificado, simplemente reconociendo que no creo que tengamos tracción porque los que lo quieren son probablemente más vocales que los que no lo hacen. Así que una solución al menos nos da una solución. –

+1

Además, ¿cuándo alguien querría este comportamiento? ¿Desea que el 'TextBox' llame a get después de que se haya configurado desde' TextBox', pero no si la propiedad está configurada desde otro lugar? No tiene sentido para mí ... –

4

Error, definitivamente.

si se trata de una "característica", que es una muy mala ...

parece que han añadido una llamada a la función get() después de que el conjunto() que se hace, incluso en el modo OneWayToSource. ¿Alguien puede explicar por qué?

también, gracias por señalar esto, se explica un problema que he tenido desde que aumenté mi proyecto a .NET 4.0 y que no podían explicar hasta ahora ...

sólo una nota al margen: Tengo resuelto esto mediante el uso de propiedades de dependencia al final.

+0

Supongo que la intención es reflejar cualquier filtrado que haya aplicado durante el conjunto, de modo que el contenido del cuadro de texto realmente refleje el valor que realmente se estableció. Lo único diferente de OneWayToSource, entonces, es que solo llamaría a get() después de aplicar un conjunto(), en lugar de llamar a get() siempre que se envíe una notificación de cambio de propiedad. –

+0

En una nota al margen, una propiedad que se comporta de esta manera (expone un getter, pero el get no tiene absolutamente nada que ver con lo que se configuró) es una propiedad bastante patológica. Parece que lo que realmente quiere es un método, pero empaquetado como una propiedad para permitir que el sistema de enlace active la llamada. Puede valer la pena investigar la creación de un Comportamiento personalizado, ya que de esa manera no tendrá que abusar del sistema de propiedad. –

+0

@ Dan Bryant: ¿Estás comentando la respuesta de David o estás respondiendo la pregunta? :) Debo agregar, que si quitas el getter, el 'TextBox' siempre estará en blanco cada vez que se'FedFocus' se levante. Seguramente esta no puede ser la forma deseada para un enlace 'OneWayToSource' o ¿qué opinas? –

6

Esto es de hecho por diseño. Por lo general, no debería crear problemas, pero la implementación de su propiedad es, al menos, poco convencional, diría yo. Los getters y setters (accesors) realmente no deberían hacer mucho más que get y set, y cada get debería ser consistente con el último conjunto correspondiente. (Lo siento, no hay fuente para esto, es lo que hemos llamado buena ciudadanía en todos los equipos de desarrollo en los que he estado).

Más detalles sobre la función aquí: http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/

+1

Es solo un ejemplo que vuelve a producir mi problema, no tengo ninguna implementación que se vea así. Además, si elimino el setter, 'TextBox' siempre estará en blanco cada vez que se llame a 'LostFocus'. Esto todavía no me parece bien –

+0

De todos modos, +1 para su explicación y el enlace –

+1

El blog karlshifflett ya no existe. – SezMe

0

¿Es este el comportamiento deseado para un Enlace OneWayToSource en .NET 4.0?

Sí. Esto se hace para que el desarrollador pueda cambiar el valor proporcionado sin convertidores torpes.

¿Cuál fue el problema con la forma en que funcionaba en 3.5?

No hay problema. La forma en que funcionaba en 3.5 no permitía corregir los valores proporcionados.

¿En qué escenarios es mejor este nuevo comportamiento?

Cuando necesite corregir los valores proporcionados. Si no lo necesita, entonces solo debe escribir getter and setter de la propiedad correcta.

public string TextProperty 
{ 
    get; 
    set; 
} 

Sin embargo, lo que puedo ver, el cambio de UpdateSourceTrigger a "PropertyChanged" preserva los valores de que se vuelva a leer (por lo que podría salir de declaración de propiedad de edad):

<StackPanel> 
    <TextBox Text="{Binding TextProperty, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/> 
    <Button/> 
</StackPanel> 

    private string m_textProperty; 
    public string TextProperty 
    { 
     get 
     { 
      return "Should not be used in OneWayToSource Binding"; 
     } 
     set 
     { 
      m_textProperty = value; 
     } 
    } 
+0

Hola Alex y gracias por tu respuesta. Voy a leer los detalles de tu respuesta más tarde esta noche para digerir. El uso de 'UpdateSourceTrigger = PropertyChanged' parece funcionar al principio, pero al examinarlo más de cerca, revela que la propiedad' Text' de 'TextBox' ya no coincide con el texto mostrado, que es un error bastante extraño causado por este comportamiento (TextBox .Text aún dirá '" No se debe usar en OneWayToSource Binding "' pero el texto que se muestra es el que escriba. –

+0

Por supuesto, TextBox.Text muestra "No debería ..." porque PIDE su valor, por lo que fuerza el enlace al valor READ, es decir, a la propiedad getter (que devuelve la cadena "Should not ..."). En realidad, si ejecutas la aplicación en modo de depuración, verás que todavía se llama a getter después de cada setter, por lo que UpdateSourceTrigger no cambia el comportamiento de 4.0, pero la UI lo maneja de manera diferente (TextBlock.Text no se restablece al valor anterior) Supongo que eventualmente el equipo de Microsoft arreglará este truco, por lo que te recomendaría que escribas getter/setter bien diseñado en lugar de usar UpdateSourceTrigger. –

+0

Has entendido por completo mi comentario. Por supuesto, no quise decir que estaba usando la falta de coincidencia entre la propiedad Text y el texto mostrado como una solución alternativa. Y la forma en que espero utilizar un enlace 'OneWayTwoSource' no implica el uso de la propiedad como una especie de" convertidor de valor ". Pero he superado esto en todas las otras respuestas, así que no hay necesidad de hacerlo de nuevo.Además, el código en la Pregunta solo está ahí para reproducir mi problema y no es algo que estoy usando, pero aparentemente no estaba claro –

2

Esto es claramente un error. Parece que alguien lo informó al menos una vez. https://connect.microsoft.com/VisualStudio/feedback/details/612444/onewaytosource-broken-in-net-4-0

Estoy de acuerdo con muchos de los otros en que una de las formas debe ser en un solo sentido.

Mi situación es complicada y el hecho de haber cambiado la funcionalidad entre las versiones de framework me ha causado un gran dolor de cabeza.

Tengo un cuadro de texto vinculado a una propiedad. Tengo un convertidor que cambia el formato sobre la marcha. EG: Ingresé EU5 en el cuadro de texto, la propiedad obtiene EU005. Conseguí que el conjunto de enlace para disparar en la propiedad cambiara ya que necesito hacer búsquedas dentro del ViewModel a medida que el usuario escribe.La nueva implementación cambia el valor del cuadro de texto a medida que escribo. Entonces, si deseo escribir EU512, no podría hacerlo fácilmente, ya que el texto del cuadro de texto seguiría cambiando.

He intentado muchas cosas: enlace explícito (en el que decide cuándo actualizar y en qué sentido). Tiene el mismo problema. Si digo, UpdateSource, lo hace, pero también vuelve a leer la propiedad y también cambia el objetivo.

Intenté OneWayToSource y tuve el mismo problema. No he encontrado forma de evitar esto sin hacer cambios molestos en mi VM. La única otra forma sería eliminar el enlace en este campo y comenzar a disparar eventos que serían terribles.

Si MS hizo que el enlace se comportase como se denomina lógicamente, mi problema desaparecería. Incluso una propiedad en el enlace para optar por la implementación .net4 y comportarse como 3.5 me funcionaría.

¿Alguien tiene alguna sugerencia sobre cómo puedo evitar esto?

0

Tuve una variación de este problema para un enlace bidireccional. Me doy cuenta de que esto no es exactamente lo mismo que el problema que se discute, pero esta respuesta salió a la cabeza durante la búsqueda y me llevó a mi solución. Espero que alguien lo encuentre útil.

Mi solución bloquea la relectura de la propiedad de respaldo pero actualizará la UI cuando la cambie alguna otra fuente. Basé mi solución en el convertidor de bloqueo en la respuesta de Rick Sladkey.

Simplemente agrega un cheque a la conversión para ver si el campo lastValue se convertiría al mismo valor de almacenamiento secundario. De lo contrario, el valor del almacén de respaldo debe haber cambiado desde otra fuente y la UI debe actualizarse.

public class MyConverter : IValueConverter 
{ 
    public object lastValue; 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (LastValue != null && MyConvertBack(LastValue).Equals(value)) 
      return lastValue; 
     else 
      return MyConvert(value); 

    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     lastValue = value; 
     return MyConvertBack(value); 
    } 

    private object MyConvertBack(Object value) 
    { 
     //Conversion Code Here 
    } 

    private object MyConvert(Object value) 
    { 
     //Conversion Code Here 
    } 
} 

En mi caso de uso particular para esto tuve un sufijo dimensiones de longitud y almacenada en un cuadro de texto (10 m, 100 mm, etc). El convertidor lo analizó en un valor doble o agregó el sufijo (según la dirección de conversión). Sin el convertidor agregaría un sufijo en cada actualización del cuadro de texto. Intentar escribir '10' daría como resultado '1m0' ya que el convertidor se ejecutaría después del primer golpe de tecla.