JASoft.org

El blog de José Manuel Alarcón Aguín. Programación web y mucho más...

MENÚ - JASoft: JM Alarcón

Permitir la validación de un control con los validadores estándar de ASP.NET

Tal y como prometí en un anterior post, voy a explicar cómo podemos conseguir que un control sea utilizable directamente por los controles de validación estándar que vienen con ASP.NET.

Ciertos controles Web (por ejemplo el control Calendar) no permiten su uso combinado con los controles de validación (estilo RequiredFieldValidator, RangeValidator, etc...), lo cual es una lástima porque para poder validar su contenido tenemos que construir nuestro propio control de validación (en fin...) o bien usar eventos de servidor para poder validarlos. Una de las ventajas de los controles de validación de ASP.NET es que ya realizan una validación en el cliente además de la que se hace en el servidor después, lo que ahorra tiempo y agiliza mucho la interfaz. Además están integrados en la validación de la página y permiten comprobar la propiedad IsValid de ésta antes de continuar con suprocesamiento, algo muy útil también.

Por otro lado es muy habitual combinar diversos controles Web con una propósito común dentro de un control de usuario (.ascx) de forma que podamos reutilizarlos en cualquier lugar de la interfaz. Al igual que (como hemos visto), por defecto, no tienen un método para establecer su foco, tampoco es posible usarlos con los validadores estándar, por lo que pierden parte de su utilidad al no poder usarlos del mismo modo que los controles más habituales.

Sería interesante poder añadir la capacidad de trabajar con los controles de validación a este tipo de controles. Vamos a ver cómo conseguirlo.

Un ejemplo real

Por ejemplo, imagina un control ASCX de selección específico de nuestra aplicación. Éste contiene un par de listas desplegables para seleccionar elementos desde la base de datos, una lista de elementos vacía inicialmente y un botón que permite añadir elementos a esta lista para su selección. Queremos colocar este control en cualquier formulario y que nos sirva, por ejemplo, para seleccionar localidades de un país. Su aspecto sería similar a este:

Este control de la imagen es uno real, usado en diversos lugares de la aplicación de uno de nuestros clientes para permitir la selección de zonas y áreas geográficas relacionadas. Tiene una funcionalidad relativamente compleja que implica el uso de bases de datos, validación de los datos seleccionados, etc... En algunos lugares de la aplicación se permite su uso de forma que no es obligatoria la selección de elementos (por ejemplo para las búsquedas, ya que si no se indica zona alguna no se restringe por dicha dimensión), y en otras zonas es necesario validar que se haya seleccionado como mínimo una zona, o incluso más de una.

Con el control creado de la manera habitual, al colocarlo en un formulario no podemos utilizar los controles de validación con él, por lo que no nos quedaría más remedio que responder a un evento de servidor (por ejemplo a la pulsación del botón de la acción principal del formulario), hacer la comprobación pertinente y mostrar un mensaje en el cliente con una etiqueta, con lo que no se integraría con la validación de los restantes controles.

Añadiendo la funcionalidad de validación

Para que un control pueda ser validado por un validador común debe implementar alguna propiedad de tipo texto que devuelva un valor interpretable por el validador. Normalmente deberíamos hacer que devolviera una cadena vacía en caso de no contener un valor correcto y así funcionará bien con el RequiredFieldValidator.

En nuestro caso hemos definido la propiedad NumItems del control ASCX que simplemente indica el número de zonas seleccionadas en el control en un momento dado:

public string NumItems
{
  get 
  {
    if (lstSeleccion.Items.Count > 0)
      return lstSeleccion.Items.Count.ToString();
    else
      return String.Empty;
    }
}

Una vez definida esta propiedad debemos indicar al runtime de ASP.NET que será ésta la utilizada para realizar las validaciones. ello se consigue usando el atributo ValidationProperty. Éste toma como parámetro el nombre de la propiedad a utilziar en las validaciones, por lo que en la definición de nuestra clase escribimos:

[ValidationProperty("NumItems"), SupportsEventValidation]
public partial class Controles_SelectorZonas : System.Web.UI.
UserControl
{

Así indicamos que los validadores deben usar la propiedad NumItems para realizar la validación. El otro atributo, SupportsEventValidation, se utiliza para indicar al runtime que nuestro control soporta los eventos de validación ya qe de otra forma no se generarían eventos de validación.

Nota: Si quisiésemos hacer algo similar con un control estándar que no soporte validación, como el calendario, tendríamos que crear una nueva clase que heredara de éste y decorarla con este atributo para poder usarlo.

Vale. Listo. Si ahora soltamos el control en un formulario Web y añadimos un control de validación veremos que... ¡no aparece nuestro control en la lista de los controles que podenmos utilizar! :-(

No pasa nada. Es un pequeño efecto secundario. Simplemente añadámoslo a mano en el atributo ControlToValidate del validador y veremos que funciona correctamente. Por ejemplo:

<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="lstDestinos"
CssClass="ErrorMsg" Display="Dynamic" ErrorMessage="Debe seleccionar al menos una zona de destino."></asp:RequiredFieldValidator>

El valor del atributo ControlToValidate, que es el nombre del control ascx que queremos validar, lo hemos metido a mano, pero funciona  perfectamente que es lo que buscábamos. Ahora nuestro complejo control de usuario se integra bien en la estructura de validación de la pa´gina y podemos trabajar con él como con cualquier otro control validable.

Super-útil ¿no? :-)

José Manuel Alarcón
Banner

Comentarios (3) -

Hombre, en el caso concreto de un calendario no es tan sencillo como en el ejemplo que pongo por una sencilla razón: el calendario no se renderiza como un control HTML estándar, sino como una tabla.

Por lo tanto la validación de cliente no te va a funcionar porque ésta espera que haya un control HTML con una propiedad 'value' que poder validar, cosa que no existe.

En cualquier caso para conseguir que el control sea validable y que aparezca incluso en la lista desplegable del control de validación, sólo debes crear una clase que herede de Calendar y agregarle una propiedad para validar, así:

namespace JASoft.Web.Controles
{
    [ValidationProperty("DateText"), SupportsEventValidation]
    public class CalendarValidable : System.Web.UI.WebControls.Calendar
    {

        public string DateText
        {
            get
            {
                if (this.SelectedDate == DateTime.MinValue)
                    return string.Empty;
                else
                    return this.SelectedDate.ToString();
            }
        }

    }
}

Ahora para usarlo en tu página lo registras en la parte superior así:

%@ Register Namespace="JASoft.Web.Controles" TagPrefix="jasoft" %

Y ya lo puedes usar, por ejemplo así:

jasoft:CalendarValidable ID="Calendar1" runat="server" /jasoft:CalendarValidable

Lo que ocurre es que no te validará nada en el cliente.

Si necesitaras conseguir tendrías que modificar el renderizado del control para añadirle un atributo 'value' a la tabla que se genera, y además modificar también el eventos javascript de cada celda de modo que antes de hacer el postback al servidor actualice dicho atributo para que sea validable. Aún así quedarían cosas por validar ya que el calendario hace un postback cada vez que pulsas en una celda. Sinceramente te saldría mejor caso construir tu propio calendario desde cero.
Ahora bien, conel código que te acabo de poner, si descativas la validación del lado del cliente en los controles de validación siempre puedes sacarle partido a la de servidor y usar Page.IsValid con el calendario sin problemas.

Espero que te haya aclarado algo.

Saludos

JM

Responder

Cuando "ocultas" un Tab, realmente sólo lo estás ocultando "lógicamente" en el servidor, pero en el cliente lo que ocurre es que, realmente, desaparece del mapa, es decir, no se genera el HTML correspondiente al mismo y por lo tanto es como no existiera. No puedes hacer validación ni nada similar porque sencillamente esos controles no existen en el lado cliente, por mucho que estén (y de hecho deban estar) en el ViewState. No tiene que ver.

Si quieres hacer la validación de todo a la vez no debes usar el control MultiView sino crearte un control Tab propio que en el cliente use capas que se ocultan y se muestran para visualizar cada "tab" ya que de esa forma, en efecto, estarán los controles allí y por lo tanto podrás validarlos.

Saludos

JM

Responder

Colombia Cesar Verano

Jorge hola, fue necesario colocar  Me.Validate() para que ejecute la validación, estoy haciendo algo mal?

Responder

Agregar comentario