2010-01-17 19 views
12

Tengo la validación conectada a un modelo que está vinculado al contenedor TextBox. Cuando se abre por primera vez la ventana, aparecen errores de validación ya que el modelo está vacío, no quiero ver errores de validación hasta que se haya enviado la ventana o el texto en el TextBox haya cambiado o haya perdido el foco.WPF TextBox Validation

Aquí es el TextBox:

<TextBox Text="{Binding 
        Path=Firstname, 
        UpdateSourceTrigger=PropertyChanged, 
        ValidatesOnDataErrors=True}" 
     Width="124" 
     Height="24"/> 

¿Cómo se puede lograr?

Respuesta

22

Esto realmente depende de su aplicación de IDataErrorInfo. Si lo basa en un Diccionario de mensajes de error, puede controlar cuándo se ejecuta la validación que se agrega a esa lista. Que normalmente se desee hacerlo desde su set de propiedad (como siempre que llame PropertyChange), aquí llamando CheckValidationState:

public string this[string columnName] 
    { 
     get 
     { 
      return ValidateProperty(columnName); 
     } 
    } 

    public Dictionary<string, string> Errors { get; private set; } 

    protected void SetError(string propertyName, string errorMessage) 
    { 
     Debug.Assert(!String.IsNullOrEmpty(propertyName), "propertyName is null or empty."); 
     if (String.IsNullOrEmpty(propertyName)) 
      return; 

     if (!String.IsNullOrEmpty(errorMessage)) 
     { 
      if (Errors.ContainsKey(propertyName)) 
       Errors[propertyName] = errorMessage; 
      else 
       Errors.Add(propertyName, errorMessage); 
     } 
     else if (Errors.ContainsKey(propertyName)) 
      Errors.Remove(propertyName); 

     NotifyPropertyChanged("Errors"); 
     NotifyPropertyChanged("Error"); 
     NotifyPropertyChanged("Item[]"); 
    } 

    protected virtual string ValidateProperty(string propertyName) 
    { 
     return Errors.ContainsKey(propertyName) ? Errors[propertyName] : null; 
    } 

    protected virtual bool CheckValidationState<T>(string propertyName, T proposedValue) 
    { 
     // your validation logic here 
    } 

También se puede incluir un método que valida todas sus propiedades (como durante una guardar):

protected bool Validate() 
    { 
     if (Errors.Count > 0) 
      return false; 

     bool result = true; 
     foreach (PropertyInfo propertyInfo in GetType().GetProperties()) 
     { 
      if (!CheckValidationState(propertyInfo.Name, propertyInfo.GetValue(this, null))) 
       result = false; 
      NotifyPropertyChanged(propertyInfo.Name); 
     } 
     return result; 
    } 

ACTUALIZACIÓN:

yo recomendaría poner el código anterior en una clase de modelo de vista de base para que pueda volver a utilizarlo. A continuación, podría crear una clase derivada de esta manera:

public class SampleViewModel : ViewModelBase 
{ 
    private string _firstName; 

    public SampleViewModel() 
    { 
     Save = new DelegateCommand<object>(SaveExecuted); 
    } 

    public DelegateCommand<object> Save { get; private set; } 

    public string FirstName 
    { 
     get { return _firstName; } 
     set 
     { 
      if (_firstName == value) 
       return; 

      CheckValidationState("FirstName", value); 

      _firstName = value; 
      NotifyPropertyChanged("FirstName"); 
     } 
    } 

    public void SaveExecuted(object obj) 
    { 
     bool isValid = Validate(); 
     MessageBox.Show(isValid ? "Saved" : "Validation Error. Save canceled"); // TODO: do something appropriate to your app here 
    } 

    protected override bool CheckValidationState<T>(string propertyName, T proposedValue) 
    { 
     // your validation logic here 
     if (propertyName == "FirstName") 
     { 
      if (String.IsNullOrEmpty(proposedValue as String)) 
      { 
       SetError(propertyName, "First Name is required."); 
       return false; 
      } 
      else if (proposedValue.Equals("John")) 
      { 
       SetError(propertyName, "\"John\" is not an allowed name."); 
       return false; 
      } 
      else 
      { 
       SetError(propertyName, String.Empty); // clear the error 
       return true; 
      } 
     } 
     return true; 
    } 
} 

En este caso estoy usando un DelegateCommand para activar la operación de almacenamiento, pero podría ser cualquier cosa que hace una llamada a un método para hacer el ahorro. Esta configuración permite que el estado vacío inicial aparezca como válido en la interfaz de usuario, pero un cambio o una llamada a Guardar actualiza el estado de validación. También puede ser mucho más general y más complicado en la forma en que realiza la validación, de modo que no todo termina en un método (aquí con algunas suposiciones sobre el tipo), pero esto se simplifica para que sea más fácil comenzar con .

+0

Gracias por la respuesta John, ¿podría ampliar su respuesta un poco por favor? Traté de implementar lo que discutiste y me perdí un poco. Gracias ... – Burt

+0

Burt - Actualicé para agregar más código para mostrar cómo una clase podría implementar esto que espero complete algunos de los agujeros para usted. ¿Eso te ayuda? –

+0

Gracias John realmente lo aprecio, fui a votar, pero de alguna manera logré eliminar un voto que no me permitió agregarlo nuevamente. – Burt

-1

Lo que hago, no sé si esta es la forma correcta (me gustaría saber, ahora hay una posibilidad), pero en el inicializador de la entidad o el modelo ejecuto todos los validadores.

7

Si está implementando IDataErrorInfo, lo he logrado comprobando valores nulos en la implementación de la lógica de validación. Al crear una nueva ventana, la comprobación de nulo evitará que se active la lógica de validación. Por ejemplo:

public partial class Product : IDataErrorInfo 
{ 
    #region IDataErrorInfo Members 

    public string Error 
    { 
     get { return null; } 
    } 

    public string this[string columnName] 
    { 
     get 
     { 
      if (columnName == "ProductName") 
      { 
       // Only apply validation if there is actually a value 
       if (this.ProductName != null) 
       { 
        if (this.ProductName.Length <= 0 || this.ProductName.Length > 25) 
         return "Product Name must be between 1 and 25 characters"; 
       } 
      } 

      return null; 
     } 
    } 

    #endregion 
} 

También, si quieres disparar la validación de TextBox.LostFocus, cambiar su unión a LostFocus, como sigue:

<TextBox Text="{Binding    
       Path=Firstname,    
       UpdateSourceTrigger=LostFocus,    
       ValidatesOnDataErrors=True}"    
    Width="124"    
    Height="24"/> 
+0

Puede ser que sea válida para algunos de los campos a ser nula, aunque lo que ocurre en ese caso? – Burt

+1

Si null es una entrada válida, la validación no arrojará un error si le indica que no lo haga. Todo depende de cómo implemente su lógica comercial. Por ejemplo, si el campo puede ser nulo o está vacío, simplemente permita que se hagan esas excepciones en su lógica. ¿Puede darnos un ejemplo de lo que todo lo que quiere verificar en un campo en particular? – Brent

4

en su archivo app.xaml, debe usar un estilo de cuadro de texto personalizado para la validación de cuadros de texto sin ningún componente de terceros.

enter image description here

<Setter Property="Template"> 
    <Setter.Value> 
     <ControlTemplate TargetType="{x:Type TextBox}"> 
      <Grid Name="test"> 
       <Border Background="{StaticResource TextBackColor}" 
         BorderBrush="#FF888888" 
         x:Name="Bd" 
         CornerRadius="1" 
         BorderThickness="1"> 
        <ScrollViewer Margin="0" x:Name="PART_ContentHost"/> 
       </Border> 
       <Image Name="ErrorImage" 
         Width="15" 
         Height="15" 
         Margin="0,0,4,0" 
         Source="Images/validate.png" 
         HorizontalAlignment="Right"> 
       </Image> 
      </Grid> 
     </ControlTemplate> 
    </Setter.Value> 
</Setter>