Validación de datos

Enlaces:

Todos los mecanismos de validación de datos están basados en la infraestructura de enlace de datos. La validación ocurre en los objetos que fungen como fuente de datos, por lo que se requiere que las expresiones de enlace {Binding} sean de modo bidireccional (Mode=TwoWay), ya que es precisamente este enlace bidireccional el que permitirá que podamos validar los datos capturados.

Estados visuales de validación

La mayoría de los controles (TextBox, RichTExtBox, ComboBox, etc.) incluyen diversos estados visuales que representan el estado de la validación de su valor enlazado.

Por ejemplo TextBox incluye 3 estados:

  1. Valid: el dato enlazado es válido.
  2. InvalidFocused: el dato es invalido y el control tiene el foco.
  3. InvalidUnfocused: el dato es inválido pero el control NO tiene el enfoque.

Validación a través de excepciones

Es la técnica más fácil de implementar y se basa en tratar un objeto de tipo Exception cuando el valor de una propiedad no es el que estamos esperando o no cumple con una regla de negocio.

Propiedad ValidatesOnExceptions.

<TextBox Text="{Binding Edad, Mode = TwoWay, ValidatesOnExceptions=True}" /}

Ahora podemos implementar alguna validación en el set de la propiedad Edad en la clase Persona.

public int Edad {
  get {return edad;}
  set {
    if (value <18) {
      throw new Exception("La edad no puede ser menor a 18 años");
    }
    edad = value;
    OnPropertyChanged("Edad");
  }
}

Esta técnica tiene algunas desventajas, como es el estar generando excepciones por una simple validación teniendo en cuenta como penaliza el rendimiento las excepciones. Por otro lado validar distintas propiedades al mismo tiempo sería complicado.

Validación a través de atributos

Añadimos el espacio de nombres System.ComponentModel.DataAnnotations que incluye distintas clases de atributos para validación. Los atributos de validación son los siguientes:

  1. StringLength: valida que una cadena contenga un número máximo de caracteres.
  2. Required: obliga a que la propiedad tenga un valor.
  3. RegularExpression: establece una expresión regular que el valor de la propiedad debe cumplir.
  4. Range: indica un rango de valores válido para el valor de la propiedad.
  5. DataType: valida que el valor de la propiedad sea de cierto tipo de dato.
  6. CustomValidation: aquí podemos invocar un método que sea el que valide el valor de la propiedad.

Todas las anteriores clases de atributos  heredan de la clase base ValidationAttribute, por lo que es perfectamente posible crear nuestros propios atributos de validación.

Ejemplo:

int edad;
[Range (18,99,ErrorMessage="La edad no es válida")] 
public int Edad {
  get {return edad; }
  set {
    edad = value;
    OnPropertyChanged("Edad");
  }
}

Clase Validator

Esta clase incluye métodos para validar tanto propiedades como objetos completos. Por lo tanto utilizaremos esta clase en el “set” de la propiedad.

El método ValidateProperty tiene dos parámetros:

  1. El valor que deseamos validar frente a los atributos de validación.
  2. Es de tipo ValidationContext que indica el nombre de la propiedad que deseamos validar.

Si el valor fuera inválido, el método ValidateProperty desencadenará una excepción de tipo ValidationException.

int edad;
[Range (18,99,ErrorMessage="La edad no es válida")] 
public int Edad {
  get {return edad; }
  set {
    Validator.ValidateProperty(value, new ValidationContext(this, null, null) {
      MemberName = "Edad" });
    edad = value;
    OnPropertyChanged("Edad");
  }
}

De esta misma forma podemos validar la propiedad Nombre.

[Required (ErrorMessage="El nombre es requerido")] 
[StringLenght(30, MinimumLenght=10, ErrorMessage="El nombre debe tener entre 10 y 30 caracteres")]
public string Nombre {
  get {return nombre; }
  set {
    Validator.ValidateProperty(value, new ValidationContext(this, null, null) {
      MemberName = "Nombre" });
    nombre = value;
    OnPropertyChanged("Nombre");
  }
}

De igual manera necesitamos la propiedad ValidatesOnException en true, en la expresión {Binding} relacionada.

<TextBox Text="{Binding Nombre, Mode=TwoWay, ValidatesOnExceptions=True}"/>

CustomValidation

public class Utilidades {
  public static ValidationResult ValidaDNI (string DNI) {
    if (DNI.Lenght != 9) {
      return new ValidationResult("El identificador fiscal debe ser de 9 caracteres"); }
    else {
      string letra = DNI.Substring(9,1);
      int resulatado;
      if (int.TryParse(letra, out resultado)) {
        return new ValidationResult("El DNI no es válido");
      else {
        return ValidationResult.Success;
      }
    }
  }
}

Para utilizar el método, añadimos el atributo CustomValidation en la propiedad IdentificadorFiscal

[CustomValidation(typeof(Utilidades),"ValidaDNI")]
public string IdentificadorFiscal {
  get {return identificadorFiscal;}
  set {
    Validator.ValidateProperty(value, new ValidationContext(this, null, null) {
      MemberName = "IdentificadorFiscal" });
    identificadorFiscal = value;
    OnPropertyChanged("IdentificadorFiscal");
  }
}

Y lógicamente también la expresión de {Binding} de la propiedad IdentificadorFiscal quedaría:

<TextBox Text="{Binding Path=IdentificadorFiscal, Mode=TwoWay, ValidatesOnExceptions=True}"/>

La clase ValidationAttribute

Es la clase base de todos los atributos de validación. La validación del identificador fiscal podría ser implementado como atributo. Para ello crearemos una clase llamada IdentificadorFiscalAttribute, que herede de la clase ValidationAttribute. Cuando se valida un atributo de validación, se invoca su método IsValid, por lo que debemos aquí implementar la lógica de validación adecuada con el atributo. En el caso de la clase IdentificadorFiscalAttribute, simplemente invocaremos la funcionalidad de la clase Utilidades que escribimos con anterioridad.

public class IdentificadorFiscalAttribute : ValidationAttribute {
  protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
    if (value ==null) {
      return ValidationResult.Success;
    }
    string identificadorFiscal = value.ToString();
    return Utilidades.ValidaIdentificadorFiscal(identificadorFiscal);
  }
}

Para utilizar este nuevo atributo, simplemente “decoramos” la propiedad que queramos que sea validada con él.

string identificadorFiscal;
[IdentificadorFiscal] public string IdentificadorFiscal {
  get {return identificadorFiscal;}
  set {
    Validator.ValidateProperty(value, new ValidationContext(this, null, null) {
      MemberName = "IdentificadorFiscal" });
    identificadorFiscal = value;
    OnPropertyChanged("IdentificadorFiscal");
  }
}

Validación a través de IDataErrorInfo

Incluye dos miembros:

  1. Error: indica si toda la entidad que implementa la interfaz tiene algún error o no (podemos hacer que simplemente devulva nulo).
  2. indizador: aquí implementaremos todas las validaciones correspondientes a cada una de las propiedades que tenga la clase en cuestión. Esta implementación, a diferencia de las que vimos anteriormente, no es intrusiva en los “set” de las propiedades, sino que separa efectivamente la validación del dato que se va a validar.

ValidatesOnDataErrors

<TextBox Text="{Binding Edad, Mode=TwoWay, ValidatesOnDataErrors=True}" />

Ahora en el indizador implementamos la validación.

public string this [string columnName] {
  get {
    switch (columnName) {
      case "Edad": break;
      default: break;
    }
  }
}

Devuelve una cadena que representa el o los mensajes recolectados por la infraestructura de validación, por lo que es muy buena idea tener un objeto adecuado donde los podamos almacenar. En este ejemplo utilizaremos un objeto de tipo StringBuilder.

public string this[string columnName] {
  get {
    StringBuilder mensajes = new StringBuilder();
    switch (columnName) {
      case "Edad":
        if (Edad<18) { mensajes.Append("La edad debe ser al menos de 18 años"); }
        if (Edad > 99) { mensajes.Append("La edad debe no pude ser mayor de 99 años");
        break;
      default: break;
    }
    return mensajes.ToString();
  }
}

Ahora podría ocurrir una excepción si introducimos un string en vez de un número en el campo de edad, para solventar esto:

<TextBox Text="{Binding Edad, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"/>

2 comentarios en “Validación de datos

  • el 29 septiembre, 2015 a las 4:34 am
    Permalink

    Hola.. Cordial saludo.. Muy bueno el tutorial, en realidad soy nuevo en la programación y me gustaría ver el ejemplo en mención ya en un proyecto, tienes el archivo para descargarlo, agradezco de antemano tu ayuda..

    Respuesta
  • el 17 mayo, 2019 a las 4:03 pm
    Permalink

    Siendo este un tutorial hubiera sido bueno tener disponible en descarga el ejemplo completo para que los que vamos empezando en WPF pudiéramos verlo. Aun asi gracias por la explicación.

    Respuesta

Deja un comentario

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.