2012-04-03 18 views
15

Tengo una ventana basada en MVVM con muchos controles, y mi modelo implementa IDataErrorInfo.Cómo forzar la actualización de los errores de validación en la Vista de ViewModel usando IDataErrorInfo?

También hay un botón SaveCommand, que realiza la validación mediante el análisis de la propiedad Model.Error.

La vista muestra el borde rojo predeterminado alrededor de los controles con errores solo cuando cambio el valor de un control en particular, o cuando notifico sobre el cambio de esa propiedad usando PropertyChanged.

¿Cómo puedo forzar que View muestre todos los errores de validación incluso cuando no toqué los controles?

Todos mis enlaces de validación incluyen ValidatesOnDataErrors=True, NotifyOnValidationError=True.

Sé que una solución es tener un cuadro agregado con todos los errores, pero preferiría mostrar los errores en cada control.

No deseo activar Model.NotifyPropertyChanged para cada propiedad vinculada de ViewModel.

Uso WPF 4.0, no Silverlight, por lo que INotifyDataErrorInfo no funcionará.

Respuesta

12

Mencione que no desea aumentar la propiedad cambiada para las propiedades a las que se une, pero esa es realmente la forma más sencilla de lograr esto. Llamar a PropertyChanged sin parámetro aumentará para todas las propiedades en su viewmodel.

Como alternativa, puede actualizar los enlaces (y revalidación de fuerza) en cualquier control de la siguiente manera:

myControl.GetBindingExpression(ControlType.ControlProperty).UpdateSource(); 
+2

Gracias por el truco con PropertyChanged. No sabía que fuera posible. He encontrado otra discusión sobre este tema: http://stackoverflow.com/questions/1135012/wpf-mvvm-can-a-single-propertychanged-update-all-the-data-bindings-of-a-datate if Alguien está interesado. Esta es una buena respuesta si alguien tiene un viewModel simple y simple.Sin embargo, tengo una vista compleja con ViewModels anidados, así que tendría que escribir código para llamar a PropertyChanged una vez por cada modelo anidado enlazado Model/ViewModel que implementa INotifyPropertyChanged – surfen

+0

. Es bueno saber este truco si uno quiere actualizar solo una parte de la vista relacionada. a un ViewModel particular – surfen

+0

myControl.GetBindingExpression (ControlType.ControlProperty) .UpdateTarget(); en realidad actualiza su validación sin actualizar su propiedad de origen. – r41n

2

La mejor solución que he encontrado hasta ahora que funciona es cambiar DataContext a nulo y volver a la instancia de ViewModel.

Esto desencadena la actualización de los controles en la vista que tiene DataContext obligado a InnerViewModel:

public void ForceUpdateErrors() { 
    var tmpInnerVM = _mainViewModel.InnerViewModel; 
    _mainViewModel.InnerViewModel = null; 
    _mainViewModel.InnerViewModel = tmpInnerVM; 
} 

Se recomienda comprobar si no se pierden datos después de este truco. Tuve un caso de que este código desencadenó la actualización de fuente para ComboBox.SelectedItem con nulo, pero logré resolverlo. Fue causado por el uso de un BindingProxy basado en recursos y el orden de propagación DataContext=null en la jerarquía de control.

1

Este 'Hack' me funcionó temporalmente, para forzar el evento InotifyChanged, simplemente asignar ese control de nuevo su propio contenido. Haga esto antes de evaluar la función HasError de enlaces. Por ejemplo, un cuadro de texto sería:

((TextBox)child).Text = ((TextBox)child).Text; 

Y a continuación, un ejemplo completo (antes de escuchar esto no es cierto MVVM, llegué directamente un asa en la parrilla para la facilidad de mostrar el código snipet)

 public bool Validate() 
    {   
     bool hasErr = false; 

     for (int i = 0; i != VisualTreeHelper.GetChildrenCount(grd); ++i) 
     { 
      DependencyObject child = VisualTreeHelper.GetChild(grd, i); 
      if (child is TextBox) 
      { 
       bool pp = BindingOperations.IsDataBound(child, TextBox.TextProperty); 
       if (pp) 
       { 

        ((TextBox)child).Text = ((TextBox)child).Text; 

        hasErr = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).HasError; 
        System.Collections.ObjectModel.ReadOnlyCollection<ValidationError> errors = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).ValidationErrors; 
        if (hasErr) 
        { 
         main.BottomText.Foreground = Brushes.Red; 
         main.BottomText.Text = BindingOperations.GetBinding(child, TextBox.TextProperty).Path.Path.Replace('.', ' ') + ": " + errors[0].ErrorContent.ToString(); 
         return false; 
        } 
       } 
      } 
      if (child is DatePicker) 
      { 
       ...      
      } 
     } 

     return true; 
    } 
Cuestiones relacionadas