2011-12-30 15 views
7

Considere el siguiente modelo de vista de la propiedad:con retraso unión de la fuente

private string _slowProperty; 
public string SlowProperty 
{ 
    get { return _slowProperty; } 
    set 
    { 
     _slowProperty = value; 
     RaisePropertyChanged("SlowProperty"); 
    } 
} 

que está unido a una caja de texto, así:

<TextBox Text="{Binding SlowProperty}" /> 

Ahora, el problema aquí es que cada vez que el valor de SlowProperty cambia, y lo hace con bastante frecuencia, el cuadro de texto iría e intentaría obtener su valor, que es bastante lento. Podría aliviar la situación utilizando el enlace asíncrono, sin embargo, eso seguiría desperdiciando ciclos de CPU.

En su lugar, lo que me gustaría tener es algo así como:

<TextBlock Text="{z:DelayedSourceBinding SlowProperty}" /> 

cual sería tratar de conseguir la unión después de un cierto retraso. Entonces, por ejemplo, si el SlowProperty cambió 5 veces seguidas, dentro de un tiempo corto, entonces solo el último texto sería visible en el cuadro de texto.

he encontrado el following project que realiza algo por el estilo, por lo que mi ejemplo que podía usar de esta manera:

<TextBox Text="{z:DelayBinding Path=SearchText}" /> 

El problema con él, es que sólo se actualiza el objetivo vinculante después un retraso. La ruta de origen, sin embargo, se evalúa y su getter se ejecuta con cada cambio de la fuente. Lo cual, en el caso de SlowProperty, seguiría desperdiciando ciclos de CPU.

He intentado hacer mi propia clase de encuadernación diferida, pero got stuck. ¿Hay alguna otra carpeta que pueda hacer algo como eso?

Para completarlo, aquí hay otros 2 proyectos que realizan tareas similares, sin embargo, ninguno de abordar el problema que estoy experimentando:

DeferredBinding - Una solución similar a DelayBinding. Sin embargo, es un poco más complejo de usar.

DelayedBindingTextBox - Implementa el enlace retrasado utilizando un control de cuadro de texto personalizado.

Gracias!

Respuesta

3

¿Por qué no resuelve este problema en el modelo de visualización? Si su propiedad cambia rápidamente, pero es lenta, podría tener una segunda propiedad 'demorada' expuesta por su modelo de vista. Puede usar un temporizador para actualizar esta propiedad 'demorada' periódicamente.

O bien, una implementación más limpia podría utilizar la función Throttle proporcionada por el marco de extensiones reactivas.

+0

Colin, excelente. Todos los problemas de CS se pueden resolver agregando otra capa de indirección. :) –

+0

Probablemente tengas razón, este es, de hecho, un problema que debería resolverse en el nivel VM. Sin embargo, aún me gustaría saber si es posible hacer un enlace personalizado, como el que describí. – VitalyB

1

Me parece que lo que realmente quieres es retrasar el punto cuando se llama a RaisePropertyChanged().
Así que lo probé, y aquí es una solución:

El XAML:

<StackPanel> 
    <TextBox Text="{Binding DelayedText, UpdateSourceTrigger=PropertyChanged}" /> 
    <TextBlock Text="{Binding DelayedText}" /> 
</StackPanel> 

El C#:

public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      this.DataContext = this; 
     } 

     private String m_DelayedText; 
     public String DelayedText 
     { 
      get 
      { 
       return m_DelayedText; 
      } 
      set 
      { 
       if (m_DelayedText != value) 
       { 
        String delayedText; 
        m_DelayedText = delayedText = value; 
        Task.Factory.StartNew(() => 
         { 
          Thread.Sleep(2000); 
          if (delayedText == m_DelayedText) 
          { 
           RaisePropertyChanged("DelayedText"); 
          } 
         }); 
       } 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
     private void RaisePropertyChanged(String _Prop) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(_Prop)); 
      } 
     } 
    } 

Usted puede comprobar que funciona mediante el establecimiento de un punto de ruptura en RaisePropertyChanged("DelayedText")

Entiendo que puede parecer bastante código para "solo" una propiedad.
Pero puede usar fragmentos de código o inyectar el código repetitivo en tiempo de ejecución usando Resharper y similares.
Y de todos modos, probablemente no lo necesite con tanta frecuencia.

Además, si tuviera que utilizar otro enfoque (por ejemplo, modificando un TextBox), tendrá que manejar cada lugar donde se enlace a la propiedad.
Al hacerlo de esta manera, en el establecimiento de la propiedad, se asegura de que todos los que acceden a esta propiedad tengan restricciones sobre las actualizaciones recibidas.

HTH,

Bab.

+0

¡Solución interesante, gracias! De hecho es un poco pesado en el código resultante, pero fue una lectura interesante. – VitalyB

2

Tuve un requisito similar hace un tiempo en el que necesitaba poder retrasar tanto el origen como el objetivo, así que creé dos extensiones de marcado llamadas DelayBinding y DelayMultiBinding. Para retrasar una actualización de la fuente se especifica UpdateSourceDelay

<TextBox Text="{db:DelayBinding SlowProperty, 
           UpdateSourceDelay='00:00:01'}" /> 

El código fuente y ejemplos de uso de DelayBinding y DelayMultiBinding puede ser downloaded here.
Si usted está interesado en los detalles de implementación, puedes echar un vistazo a mi blog sobre él aquí:
DelayBinding and DelayMultiBinding with Source and Target delay

1

Es de destacar que, a partir de .Net 4.5, un delay property se ha añadido al marco, lo que permite establece la cantidad de retraso de vinculación en milisegundos. En el ejemplo de Microsoft, se enfatiza el modo twoway pero el retardo de enlace funciona en cualquier modo vinculante.

Por ejemplo, utilicé esto en una cuadrícula de datos donde el elemento/valor seleccionado tuvo que ser cambiado tanto desde un cuadro de texto dentro de un control de usuario personalizado como obviamente desde dentro de la cuadrícula de datos. Debido a razones que no mencionaré aquí, el cuadro de texto tenía que vincularse a una propiedad diferente del modelo de vista, pero ambas propiedades tenían que tener el mismo valor al final del día, y cualquier cambio en una de ellas tenía que ser reflejado en el otro. Cuando el valor seleccionado cambió en la cuadrícula de datos, el cuadro de texto también se tuvo que actualizar, y verifiqué los cambios de valores reales en el sistema de la propiedad enlazada SelectedValue, para evitar el bucle infinito. Cuando el cambio fue demasiado rápido, se produjo un error al guardar los datos en la fuente cuando el texto cambió dentro del cuadro de texto que fue modificado por el configurador SelectedValue. Un niño de dos demora de fotogramas solucionó el problema sin soluciones complejas y sin la sensación de la interfaz de usuario demasiado lag:

SelectedValue="{Binding SampleNumberSelect, Mode=OneWayToSource, Delay=33}" 

Esto es muy conveniente y le ahorra la molestia de implementar cualquier cambio en el modelo de vista, lo que innecesariamente el desorden de la código, que incluye tener que eliminar los temporizadores al cerrar la ventana. Por supuesto, ni siquiera tiene que ser utilizado en un escenario relativamente complejo como el mío, pero podría ser útil evitar que el código pesado de CPU/recursos se ejecute innecesariamente con cada pequeño cambio en la UI.