Validation Rules en WPF

Siguiendo con el DataBinding de WPF vamos ha hablar de ValidationRules.Una regla de validación es una porción de código usada para validar un dato en el target, antes de que se actualice el source, es decir se utiliza para que se realice la validación en la Interfaz de Usuario antes de que se modifique el source (por ejemplo una propiedad enlazada), pero mejor vamos a verlo con ejemplos

El código de validación es realizado en una clase que extiende ValidationRule, haciendo un override del método Validate. Existe una regla de fabrica denominada ExceptionValidationRule, que nos da la posibilidad de controlar la introducción de datos erróneos como en este caso.

<Window ... xmlns:local="clr-namespace:WithBinding">
   <Window.Resources>
      ...
      <local:AgeToForegroundConverter x:Key="ageConverter" />
   </Window.Resources>
   ...
   <TextBox ...
       Foreground="
          {Binding
             Path=Age,
             Converter={StaticResource ageConverter}}">
       <TextBox.Text>
          <Binding Path="Age">
             <Binding.ValidationRules>
                <ExceptionValidationRule />
             </Binding.ValidationRules>
          </Binding>
       </TextBox.Text>
   </TextBox>
   ...

En este caso en el Textbox de Edad hemos puesto la regla de validacion ExceptioValidationRule nos comprobara que lo que metamos pueda ser transformado a int porque la propiedad Age a la que esta enlazado es de tipo int y no puede hacer la conversión.

Cuando un error de validación ocurre, un objeto ValidationError es creado.Este contiene el objeto usado para desplegar el mensaje de error, en general a nivel de la

UI. En el caso de la ExceptionValidationRule, el error de validación esta en la propiedad ErrorContent de este objeto que a su vez contiene la propiedad Message de la excepción capturada por esta regla. Para lograr acceder a esta información sobre los errores, podemos escuchar por el evento ValidationError, attacheado al elemento de UI en cuestión que causa el error.

 

// Window1.cs
...
public Window1( ) {
   InitializeComponent( );
   this.birthdayButton.Click += birthdayButton_Click;
   Validation.AddErrorHandler(
      this.ageTextBox,
      ageTextBox_ValidationError);
}
void ageTextBox_ValidationError(
      object sender, 
      ValidationErrorEventArgs e) {
   MessageBox.Show((string)e.Error.ErrorContent, 
                   "Validation Error");
}
 

Debemos de decir que el mecanismo anterior funciona, solo si la propiedad NotifyOnValidationError esta con valor True en el Binding del elemento en cuestión

<!-- Window1.xaml -->
...
<TextBox Name="ageTextBox" ...>
   <TextBox.Text>
      <Binding Path="Age" 
               NotifyOnValidationError="True">
         <Binding.ValidationRules>
            <ExceptionValidationRule />
         </Binding.ValidationRules>
      </Binding>
   </TextBox.Text>
</TextBox>
...

Al ejecutar esto con un error de validación, obtenemos…

image

Si bien lo anterior funciona bien, el mensaje no es muy amigable para el usuario final. Podemos mejorarlo un poco customizando la manera de mostrar el error, como vemos en el siguiente código, en el que creamos una regla de validación para comprobar el rango de edad:

public class NumberRangeRule : ValidationRule {
   int min;
   public int Min {
      get { return min; }
      set { min = value; }
   }
   int max;
   public int Max {
      get { return max; }
      set { max = value; }
   }
   ...
   public override ValidationResult Validate(
      object value, 
      System.Globalization.CultureInfo cultureInfo) {
      int number;
      if( !int.TryParse((string)value, out number) ) {
         return new ValidationResult(
            false,
            "Invalid number format");
      }
      if( number < min || number > max ) {
         return new ValidationResult(
            false,
            string.Format(
                "Number out of range ({0}-{1})", 
                min, max));
      }
      return new ValidationResult(true, null);
   }
}

Enganchamos la regla en el XAML como antes…

 

<TextBox ...
   Foreground="
     {Binding Path=Age,
        Converter={StaticResource ageConverter}}">
   <TextBox.Text>
      <Binding Path="Age">
          <Binding.ValidationRules>
              <local:NumberRangeRule Min="0" 
                                     Max="128" />
          </Binding.ValidationRules>
      </Binding>
   </TextBox.Text>
</TextBox>

 

image

Podemos mejorar aun mas, colocando el mensaje de error en un tooltip sobre el elemento que causo el error. Utilizando databinding sobre la propiedad ErrorContent podemos mostrar el error:

<TextBox
   Name="ageTextBox" ...
   ToolTip=
      "{Binding
         ElementName=ageTextBox,
         Path=(Validation.Errors)[0].ErrorContent}">
   <TextBox.Text>
     <Binding Path="Age">
        <Binding.ValidationRules>
           <local:NumberRangeRule Min="0" Max="128" />
        </Binding.ValidationRules>
     </Binding>
   </TextBox.Text>
</TextBox>

En el ejemplo anterior, el tooltip esta asociado al primer elemento de la attached property Errors. Si no hay errores, el tooltip esta vacío, cuando hay errores, se toma la información y se despliega. Ya no necesitamos el NotifyOnValidationError en true, ya que el cambio en la colección y el binding, mantiene los elementos sincronizados

 

image

8 comentarios sobre “Validation Rules en WPF”

  1. Pues sí, la cosa es que no pone cosas raras, la mayoría si lo traduces, pone que lo lee en un pis pas o que es interesante, pero podía hablar al menos en Inglés que lo entienda más gente, y no cambiar tanto de nick y de web de enlace.

    Por lo demás, Oskar, nos estás poniendo al día de WPF 😀

    Saludos y gracias.

    Francisco J.

  2. Es interesante este blog. Aunque a estas alturas ya tengo bastante experiencia con WPF y de momento estás con los conceptos básicos, no hay casi nada en la red que no sea en inglés, seguro que mucha gente aprende gracias a ti. Sigue así :).

  3. Todo esta muy bien. Pero como controlas el focus?.. Es decir que no te deje brincar al siguiente texbox si hay una validacion que no se cumple?

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *