2011-02-10 19 views
13

Tengo una serie de controles que están enlazados a valores que cambian aproximadamente cada segundo. De vez en cuando, necesito "pausar" los controles, para que no actualicen sus enlaces de datos (en cualquier dirección). Luego, más adelante, debo "quitar el relevo" de los controles para que puedan actualizar la fuente de datos con sus valores y recibir actualizaciones futuras de la fuente de forma normal. ¿Cómo logro esto?Suspend Databinding of Controls

Muestra Encuadernación:

<TextBox Text="{Binding UpdateSourceTrigger=LostFocus, Mode=TwoWay, Path=myData}"> 
+0

¿Puede mostrar cómo vincula valores a un control? ¿Lo haces directo en xaml o en código? –

Respuesta

12

No es necesario que suspenda el enlace. Otra manera, y posiblemente más simple, de hacer esto es suspender la notificación de cambio en el modelo de vista. Por ejemplo:

private HashSet<string> _ChangedProperties = new HashSet<string>(); 

private void OnPropertyChanged(string propertyName) 
{ 
    if (_Suspended) 
    { 
     _ChangedProperties.Add(propertyName); 
    } 
    else 
    { 
     PropertyChangedEventHandler h = PropertyChanged; 
     if (h != null) 
     { 
     h(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

private bool _Suspended; 

public bool Suspended 
{ 
    get { return _Suspended; } 
    set 
    { 
     if (_Suspended == value) 
     { 
     return; 
     } 
     _Suspended = value; 
     if (!_Suspended) 
     { 
     foreach (string propertyName in _ChangedProperties) 
     { 
      OnPropertyChanged(propertyName); 
     } 
     _ChangedProperties.Clear(); 
     } 
    } 
} 

Esto (si está depurando y probado, que no he hecho) dejar de subir las PropertyChanged eventos cuando Suspended se establece en true, y cuando Suspended se establece en false nuevo que aumentará el evento por cada propiedad que cambió mientras estaba suspendida.

Esto no evitará que los cambios en los controles limitados no actualicen el modelo de vista. Le presento que si le permite al usuario editar las propiedades en la pantalla al mismo tiempo que las está cambiando en segundo plano, hay algo que debe analizar más de cerca, y no es vinculante.

+1

Esta respuesta (y la respuesta de Aaron) funcionó; la clave es no preocuparse por el enlace, pero deshabilite el PropertyChanged. – GWLlosa

0

Si se mantiene una referencia a la vista en la clase del controlador que podría desencadenar un evento desde el modelo de vista cuando se desea suspender databinsing que tiene el controlador clara la DataContext de la vista. y cuando esté listo para comenzar a enviar datos de recepción de nuevo, restablezca las Vistas DataContext al Modelo de Vista.

3

Para tratar con el conjunto de origen, UpdateSourceTrigger es Explicit.

<TextBox Name="myTextBox" Text="{Binding UpdateSourceTrigger=Explicit, Mode=TwoWay, Path=myData}"> 

Luego, en código detrás de la referencia de un servicio que puede hacer frente a la actualización actual, definido por sus condiciones.

BindingExpression be = myTextBox.GetBindingExpression(TextBox.TextProperty); 
be.UpdateSource(); 

Esto le permitirá especificar en qué punto los datos vuelven a la fuente del destino.

El objetivo puede abordarse realizando una llamada al mismo servicio al que se hace referencia que tiene el conocimiento sobre cuándo llamar al evento INotifyPropertyChanged.PropertyChanged dentro de su ViewModel.

class Data : INotifyPropertyChanged 
    { 
     Manager _manager; 

     public Data(Manager manager) 
     { 
      _manager = manager; 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     String _info = "Top Secret"; 
     public String Information 
     { 
      get { return _info; } 
      set 
      { 
       _info = value; 

       if (!_manager.Paused) 
       { 
        PropertyChangedEventHandler handler = PropertyChanged; 
        if (handler != null) 
         handler(this, new PropertyChangedEventArgs("Information")); 
       } 
      } 
     } 
    } 
+0

¿Hay alguna forma de cambiar programáticamente la expresión de enlace UpdateSourceTrigger de LostFocus a Explicit y viceversa? ¿O debo dejarlo como explícito todo el tiempo? – GWLlosa

+0

En realidad, lo intenté y no funcionó. Explícito aún hace que reciba actualizaciones de la fuente, simplemente no parece enviar actualizaciones a la fuente. – GWLlosa

+0

@GWLlosa Los cambios se llevarán a cabo dentro del control ya que puede escribir o hacer una serie de cosas dentro del control; sin embargo, no serán devueltos a la fuente hasta que llame a UpdateSource(); Por otro lado, a menos que active INotifyPropertyChanged.PropertyChange, la actualización no se propagará al control –

2

En primer lugar es necesario crear explícita de unión:

Binding binding = new Binding("Content"); 
binding.Source = source; 
binding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus; 
binding.Mode = BindingMode.TwoWay; 
txtContent.SetBinding(TextBox.TextProperty, binding); 

A continuación, cuando se necesita hacer una pausa en la unión que necesita destruir antigua unión y crear la unión con el disparador explícita (en este caso la nueva unidireccional de dos vías que origen de enlace no se actualizará cuando alguna propiedad ha sido cambiado):

BindingOperations.ClearBinding(txtContent, TextBlock.TextProperty); 
Binding binding = new Binding("Content"); 
binding.Source = source; 
binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit; 
binding.Mode = BindingMode.OneWay; 
txtContent.SetBinding(TextBox.TextProperty, binding); 

cuando tenga que volver a la unión de dos vías que pueden fuente de actualización explícita (si lo necesita) que dest roy unidireccional y crea una unión twoway.

BindingExpression be = txtContent.GetBindingExpression(TextBox.TextProperty); 
be.UpdateSource(); 
BindingOperations.ClearBinding(txtContent, TextBlock.TextProperty); 

Binding binding = new Binding("Content"); 
binding.Source = source; 
binding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus; 
binding.Mode = BindingMode.TwoWay; 
txtContent.SetBinding(TextBox.TextProperty, binding); 
+0

Esto no impide que el valor se actualice desde la fuente, ya que UpdateSourceTrigger solo afecta cuando se guarda en la fuente algo ingresado (por el usuario). Por lo tanto, el valor seguirá cambiando mientras el usuario intenta cambiarlo. – aliceraunsbaek

0

Si el control que desea suspender tiene un DataContext (ViewModel) de su propiedad, simplemente guárdelo y anule el DataContext.

Si el control tiene un DataContext heredado, al establecer que DataContext del control sea nulo, se bloqueará la herencia. Luego, para reanudar las actualizaciones de enlace, use el método ClearValue para borrar DataContext DependencyProperty para que la herencia vuelva a activarse.

Puede hacerse elegante y utilizar VisualBrush para tomar una captura de pantalla del control que está suspendiendo antes de borrar su DataContext, para que el usuario no vea el control en blanco.

0

Mi solución terminó de la siguiente manera para evitar que el texto se actualice mientras el usuario intenta cambiarlo.

XAML:

<TextBox Grid.Row="0" Grid.Column="1" TextAlignment="Right" VerticalAlignment="Center" Text="{Binding Path=MinimumValueInDisplayUnit, StringFormat=0.########}" MinWidth="100" Margin="4" GotFocus="TextBox_OnGotFocus" LostFocus="TextBox_OnLostFocus"/> 
<TextBox Grid.Row="0" Grid.Column="2" TextAlignment="Right" VerticalAlignment="Center" Text="{Binding Path=MaximumValueInDisplayUnit, StringFormat=0.########}" MinWidth="100" Margin="4" GotFocus="TextBox_OnGotFocus" LostFocus="TextBox_OnLostFocus"/> 

Código atrás:

private void TextBox_OnGotFocus([CanBeNull] object sender, [CanBeNull] RoutedEventArgs e) 
    { 
     TextBox tb = sender as TextBox; 
     if (tb == null) return; 
     BindingExpression expression = tb.GetBindingExpression(TextBox.TextProperty); 
     if (expression == null) return; 
     // disable updates from source 
     BindingOperations.ClearBinding(tb, TextBlock.TextProperty); 
     tb.SetBinding(TextBox.TextProperty, new Binding(expression.ParentBinding.Path.Path) { Mode = BindingMode.OneWayToSource, UpdateSourceTrigger = UpdateSourceTrigger.Explicit , FallbackValue = tb.Text}); 
    } 

    private void TextBox_OnLostFocus([CanBeNull] object sender, [CanBeNull] RoutedEventArgs e) 
    { 
     TextBox tb = sender as TextBox; 
     if (tb == null) return; 
     BindingExpression expression = tb.GetBindingExpression(TextBox.TextProperty); 
     if (expression == null) return; 
     // send current value to source 
     expression.UpdateSource(); 
     // enable updates from source 
     BindingOperations.ClearBinding(tb, TextBlock.TextProperty); 
     tb.SetBinding(TextBox.TextProperty, new Binding(expression.ParentBinding.Path.Path) { Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.LostFocus }); 
    } 

Tenga en cuenta que le asigno el texto actual como el valor de retorno de la OneWayToSource unión a tener un valor inicial (de lo contrario el campo de texto habría vacío una vez enfocado)