VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 262 Bases de datos con Visual Basic End Sub Protected Sub dsFormView_Deleting(ByVal sender As Object, _ ByVal e As System.Web.UI.WebControls.SqlDataSourceCommandEventArgs) _ Handles dsFormView.Deleting 'Test for Order Details records End Sub Protected Sub dsFormView_Updating(ByVal sender As Object, _ ByVal e As System.Web.UI.WebControls.SqlDataSourceCommandEventArgs) _ Handles dsFormView.Updating 'Substitute NULL for 1/1/20599 If e.Command.Parameters("@ShippedDate").Value.ToString.Contains("1/1/2099") Then e.Command.Parameters("@ShippedDate").Value = DBNull.Value End If End Sub End Class El evento DataBound se dispara cuando todas las filas de DataSource están pobladas. El método FormView.DataItem(ColumnName) devuelve el último valor vinculado de la fila de datos seleccionada actualmente. Las sentencias DimvarNameAsControlType = CType(FormView.FindControl(ControlId), ControlType) devuelve una referencia al control que permite definir sus valores de propiedad, por ejemplo Text o Enabled. El manejador de evento también desactiva el botón Delete para los pedidos que ya se han enviado. 7.4.3 Editar, añadir y borrar registros El control FormView es mejor elección para editar registros que DataList, ya que FormView sólo muestra un elemento. Se puede crear una plantilla EditItem o InsertItem rápidamente en el modo Código del editor copiando y pegando el nodo <ItemTemplate> y sus hijos. (Para borrar una fila no se necesita ninguna plantilla). El diseñador renombra automáticamente las etiquetas con valores Id duplicados en LabelN, donde N es un número secuencial. Renombre el <ItemTemplate> que ha copiado y póngale el nombre <EditItemTemplate>, y sustituya todas las instancias de Label en los nodos hijo de <EditItemTemplate> por TextBox para completar la nueva plantilla. Si la plantilla Items tiene bordes, desactive la propiedad cell border de la tabla para eliminarlos. Otra alternativa es dar a la propiedad BorderColor el valor BgColor de la tabla o el BackColor del FormView. La figura siguiente muestra el FormView de la figura anterior con la plantilla EditItem activa. Una vez finalizado el diseño de la plantilla EditItem, copie y pegue en modo Código el nodo <EditItemTemplate> y sus hijos y renombre la copia como <InsertItemTemplate> para que se puedan añadir nuevos ítems. 262 VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 263 Trabajar con las fuentes de datos y controles vinculados de ASP.NET 2.0 7.4.4 Añadir botones de comando Los controles FormView definen un conjunto de expresiones de modo y acción para activar las plantillas EditItem o InsertItem, cancelar operaciones de edición o inserción y ejecutar los comandos de DataSource UpdateCommand, InsertCommand o DeleteCommand. Para añadir un control Button, LinkButton o ImageButton a una plantilla y asignarles una acción hay que escribir el nombre de la acción en el cuadro de texto de la propiedad CommandName. A diferencia de otros botones similares en las plantillas DataList, para activar plantillas o actualizar, insertar o borrar ítems, no se necesita código de manejador de evento. Aquí vemos cómo utilizar los tres modos para activar las plantillas: Q Q Q Edit – activa la plantilla EditItem. Añade un botón Edit o Update a la plantilla Item por defecto, con el valor edit o Edit en su propiedad CommandName. New – activa la plantilla InsertItem. Añade un botón New a la plantilla por defecto Item con el valor new o New en su propiedad CommandName. Cancel – reactiva la plantilla Item por defecto. Añade botones Cancel a las plantillas EditItem e InsertItem con el valor cancel o Cancel en su propiedad CommandName. Las siguientes acciones son para ejecutar comandos: Q Q Update – ejecuta UpdateCommand de DataSource y activa la plantilla Item. Añade un botón Save o Update a la plantilla EditItem, con el valor update o Update en su propiedad CommandName. Insert – ejecuta el InsertCommand de DataSource y activa el comando Item. Añade un botón Save o Insert a la plantilla InsertItem con el valor insert o Insert en su propiedad CommandName. 263 VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 264 Bases de datos con Visual Basic Q Delete – ejecuta el DeleteCommand de DataSource. Añade un botón Delete o Remove a la plantilla Item por defecto con el valor delete o Delete en su propiedad CommandName. La figura siguiente muestra la página FormView.aspx con la plantilla InsertItem activada y la entrada de datos parcialmente completada. Los controles de paginación quedan ocultos en el modo Insert. 7.5 El control GridView El control GridView, que substituye el DataGrid de ASP.NET 1.x, simula el control de formulario Windows DataGridView hasta cierto punto, si se considera las limitaciones respecto al navegador de los controles del servidor HTML. El proceso de añadir un GridView a un formulario es similar al de un DataList o FormView. Arrastre el control GridView hasta una página y seleccione una fuente de datos existente o especifique una nueva. La figura siguiente muestra un control GridView paginado, de sólo lectura, poblado por dsOrders SqlDataSource y con un campo Select Command autogenerado. Para añadir un campo Select Command para un control GridViews de sólo lectura se ha de seleccionar la casilla de verificación Habilitar paginación de la etiqueta inteligente GridView. Al seleccionar Habilitar paginación se añade una paginación numérica por defecto para el formulario. El cuadro de verificación Habilitar ordenación subraya y cambia el color de las cabeceras de columna para indicar el tipo de clasificación. Puede desactivar la clasificación de los campos seleccionados borrando el valor de su propiedad SortExpression en el cuadro de diálogo Campos. Los GridViews con DataSources actualizables añaden casillas de verificación Habilitar edición y Habilitar eliminación como muestra la figura posterior. Una limitación seria de los GridViews es que no permiten 264 VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 265 Trabajar con las fuentes de datos y controles vinculados de ASP.NET 2.0 añadir nuevos elementos. Para ésto se ha de utilizar o bien un DetailsView o un FormView, que pueden estar en la misma página o en otra diferente. Los controles GridView soportan estos siete tipos de campo: Q CommandFields – corresponden a los modos de FormView. Los CommandFields existentes son: Select, Edit, Cancel, Update y Delete, que disparan los eventos ItemCommand, SelectedIndexChanging, SelectedIndexChanged, RowEditing, RowCancelingEdit, RowUpdating, RowUpdated, RowDeleting y RowDeleted. El control por 265 VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 266 Bases de datos con Visual Basic defecto para CommandFields es un botón de texto Link. Los botones o imágenes convencionales se pueden sustituir definiendo el valor Button o Image para la propiedad ButtonType del campo. Q BoundFields – muestran valores en controles Label por defecto. Si se pulsa el botón Editar de una fila, los controles Label de columnas editables cambian a Cuadros de texto. La anchura de los cuadros de texto es fija, para cambiarla hay que cambiar el BoundField por un TemplateField. Q CheckBoxFields – muestran y editan valores binarios, como 0 y 1 o False y True. Q ButtonFields – muestran controles Button convencionales. Q Q Q HyperlinkFields – muestran texto y proporcionan un campo adicional oculto para navegar por la página. Los campos Select command se pueden sustituir por campos de hipervínculo para cargar y editar páginas. ImageFields – muestran gráficos de las columnas image o varbinary del SQL Server, o archivos XML de imágenes codificadas. TemplateFields – permiten personalizar el formateo de los cuadros de texto o substituir otros controles, como DropDownLists, para la edición. 7.5.1 Convertir campos BoundFields en campos EditItemTemplate Los cuadros de texto con un ancho automático son suficientes para los tests iniciales, pero normalmente habrá que ajustar la anchura para que quepa un GridView, necesario para la edición de los datos. La figura siguiente muestra la página EditableGridView.aspx con una fila en el modo edición. Todas las columnas de esta página, excepto Order ID, que es de sólo lectura, son TemplateFields. Las plantillas Empl. ID y Ship Via especifican DropDownLists vinculados para definir los valores de las columnas numéricas. El cuadro de texto Cust. ID es de sólo lectura porque no es habitual reasignar un pedido a un cliente diferente. Para convertir un BoundField en un TemplateField, abra la etiqueta inteligente GridView y pulse el vínculo Editar columnas para abrir el cuadro de diálogo Campos. Seleccione en la lista Campos seleccionados el campo vinculado que quiere convertir, pulse el vínculo Convertir este informe en Template Field y pulse después Aceptar. El proceso de conversión añade un ItemTemplate con un control Label y un EditItemTemplate con un control de cuadro de texto e ítems vacíos AlternatingItemTemplate, HeaderTemplate y FooterTemplate, bajo una cabeceras ColumnName para cada una de las columnas convertidas (ver figura siguiente). A continuación vemos el código fuente reformateado para las columnas de sólo lectura Order ID y Customer ID: <Columns> <asp:BoundField ReadOnly="True" HeaderText="Order ID" InsertVisible="False" DataField="OrderID" SortExpression="OrderID"> <ItemStyle HorizontalAlign="Right"></ItemStyle> </asp:BoundField> 266 VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 267 Trabajar con las fuentes de datos y controles vinculados de ASP.NET 2.0 <asp:TemplateField SortExpression="CustomerID" HeaderText="Cust. ID"><EditItemTemplate> <asp:TextBox ID="txtCustomerID" Runat="server" Width="46px" % Text='<%# Bind("CustomerID") %>' ReadOnly="True"></asp:TextBox> </EditItemTemplate> <ItemStyle HorizontalAlign="Left"></ItemStyle> <ItemTemplate> <asp:Label Runat="server" Text='<%# Bind("CustomerID") %>' ID="Label3"></asp:Label> </ItemTemplate> </asp:TemplateField> </Columns> (ver figura siguiente) 7.5.2 Remplazar cuadros de texto por listas desplegables para la edición Una buena práctica de diseño es proporcionar listas desplegables vinculadas para definir valores de clave foránea con un número limitado de opciones. Para sustituir un cuadro de texto por una lista desplegable, cree una fuente de datos para la lista y borre el cuadro de texto. Arrastre un control DropDownList desde el cuadro de herramientas, defina su DataSource, sus campos de muestra y de valor, y después vincule la propiedad SelectedValue a la columna de clave foránea escribiendo Bind("DataColumnName") en el cuadro de texto para la expresión de código del cuadro de diálogo ListName- 267 VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 268 Bases de datos con Visual Basic DataBindings (ver la figura siguiente). El método Bind del GridView remplaza al método Eval de los controles DataList y FormView. Veamos el código fuente reformateado de la plantilla para la columna Empl. ID: <Columns> <asp:TemplateField HeaderText="Empl. ID"><EditItemTemplate> <asp:DropDownList ID="ddlEmployee" Runat="server" Height="22px" Width="94px" DataSourceID="dsEmployees" DataValueField="EmployeeID" DataTextField="LastName" SelectedValue='<%# Bind("EmployeeID") %>'> </asp:DropDownList> </EditItemTemplate> <ItemStyle HorizontalAlign="Center"></ItemStyle> <ItemTemplate> <asp:Label Runat="server" Text='<%# Bind("EmployeeID") %>' ID="Label2"></asp:Label> </ItemTemplate> </asp:TemplateField> </Columns> 268 VisualBasic2005_07.qxp 02/08/2007 16:29 PÆgina 269 Trabajar con las fuentes de datos y controles vinculados de ASP.NET 2.0 269 VisualBasic2005_08.qxp 02/08/2007 16:30 PÆgina 271 Capítulo 8 Aplicar técnicas avanzadas con ASP.NET 2.0 Este capítulo expande el horizonte de desarrollo con VB.NET VB 2005 más allá de las sencillas páginas Web que sólo contienen datos y algunos controles Web vinculados. Este capítulo trata aspectos más avanzados de ASP.NET 2.0, que le enseñarán a sacar el mejor partido de los controles Web y las funciones de VS 2005, algunos nuevos y otros actualizados, que vemos a continuación: Y Y Data validation: validación de datos con los controles RequiredFieldValidator, RangeValidator, RegularExpressionValidator, CompareValidator, CustomValidator y ValidationSummary. ObjectDataSources: fuentes de datos de objeto, basadas en tablas de juegos de datos tipificados, definidos en los archivos DataSet.xsd o referencias a bibliotecas de clase y objetos de negocios habituales con campos Nullable(OfDataType). No conseguirá las habilidades necesarias para diseñar sitios Webs escalables y funcionales, con fuentes de datos de ejemplo, si se limita a unos cuadros de texto y cinco o diez filas o grupos de elementos. Con las tablas Orders y Order Details como fuentes de datos para la mayoría de los proyectos de ejemplo expuestos en este capítulo, podrá abordar muchos de los aspectos a los que tendrá que enfrentarse en el desarrollo de páginas Web de producción con VS 2005. 8.1 Validar entradas en controles vinculados a datos Validar las entradas del usuario antes de enviar los valores editados al servidor de la base de datos evita acceder innecesariamente al servidor, reduce la carga del mismo e incrementa la escalabilidad de la aplicación. Si sus controles vinculados a datos conectan con un componente lógico de acceso a datos que refuerza las reglas de negocios, la validación por parte del cliente de los campos requeridos y los formatos de datos, reduce el tráfico en la red y el consumo de recursos. Todos los formularios Web y de producción de Windows deberían permitir la validación de los datos entrados por el usuario desde el cliente, independientemente de la arquitectura de acceso de datos que se adopte. 271 VisualBasic2005_08.qxp 02/08/2007 16:30 PÆgina 272 Bases de datos con Visual Basic ASP.NET cuenta con dos métodos para validar las entradas del usuario mediante controles vinculados: los controles de validación del servidor o el código añadido a los manejadores de evento ControlId_Updating o ControlId_Inserting. Los controles de validación desde el servidor muestran mensajes de error de validación cuando el usuario escribe o selecciona un valor que no supera el test de validación y pasa al siguiente control. El código del manejador de evento puede mostrar uno o más mensajes de error de validación en un cuadro de texto, pero los mensajes de error no aparecen hasta que el usuario finaliza el proceso de edición y pulsa un botón de actualización o de entrada de datos. Retrasar la valiadación hasta el momento de enviar los datos puede ser frustrante para los usuarios, especialmente después de una larga sesión de introducción de datos. 8.2 Los controles de validación de ASP.NET 2.0 ASP.NET 2.0 cuenta con los mismos controles de validación por parte del servidor que ASP.NET 1.1. Los controles de validación no se limitan a los campos vinculados, también se puede asisgnar un validador a cualquier control de servidor que acepte la entrada de datos por parte del usuario. Para validar columnas GridView o campos DetailsView se necesita una plantilla EditItem por cada ítem que se quiere validar. A continuación damos una descripción breve de los seis controles de validación de ASP.NET: Y Y Y 272 RequiredFieldValidator: comprueba si el control especificado por su propiedad Id contiene algún valor que no concuerde con el de la propiedad InitialValue, que tiene un string vacío por defecto. Dando uno de los valores de una DropDownList con [SelectanItem] como primer miembro de la colección de Items y definiendo True para AppendDataBoundItems, el valor inicial InitialValue queda especificado como [SelectanItem]. Si el valor entrado no concuerda con InitialValue, el validador muestra el valor de su propiedad Text o ErrorMessage en letras rojas, a la derecha o por debajo del control. RangeValidator: comprueba si el valor de un control específico queda dentro del rango de valores definidos para las propiedades MinValue y MaxValue, cuyo tipo se especifica en la propiedad DataType. Esos dos valores pueden ser constantes String, Integer, Double, Date o Currency. El mensaje de error aparece cuando los valores introducidos o seleccionados quedan fuera del margen especificado. El control RangeValidator no comprueba los controles vacíos, por lo que hay que añadir un RequiredFieldValidator al control que se especifique. RegularExpressionValidator: comprueba que el texto del control especificado sea conforme a una expresión regular que usted debe escribir como valor de la propiedad ValidationExpression. Por ejemplo, la RegularExpression [A-Z]{5} valida una entrada CustomerID si contiene cinco letras mayúsculas. Otra alternativa, es pulsar el botón builder de ValidationExpression para seleccionar una de las expresiones regulares estándar, con formato de dirección de e-mail, URL, número de teléfono, código postal o número de la seguridad social o del carnet de identidad. Hay que añadir también un RequiredFieldValidator para comprobar los valores vacíos. VisualBasic2005_08.qxp 02/08/2007 16:30 PÆgina 273 Aplicar técnicas avanzadas con ASP.NET 2.0 Y Y Y CompareValidator: comprueba si el valor del control especificado es mayor, menor, igual, menor o igual, mayor o igual que el de otro control cuyo valor Id se especifica como valor de la propiedad ControlToCompare. CompareValidator comprueba el mismo tipo de datos que RangeValidator. Hay que añadir un RequiredFieldValidator para comprobar los valores vacíos. CustomValidator: permite comprobar el control especificado con una función de JScript o VBScript, la función ClientValidationFunction y un manejador VB.NET similar que hay que escribir para el evento OnServerValidate. La función ClientValidationFunction proporciona validación por parte del cliente y el manejador de evento OnServerValidate se ejecuta cuando el usuario envía datos a la página. ValidationSummary: proporciona la herramienta para mostrar los valores de la propiedad ErrorMessage de todos los controles de validación actuales en un solo cuadro de texto o un cuadro de lista. Otra alternativa, u opción adicional, es mostrar los errores en un cuadro de mensaje para usuarios con IE 4.0 o posteriores. El resumen de los mensajes de error no aparece hasta que el usuario envía el formulario. La propiedad CausesValidation de los controles Edit y New LinkButton, tiene por defecto el valor True, lo que activa todos los controles de la plantilla EditItem y, en el caso de DetailsView, en la plantilla InsertItem. 8.2.1 La nueva propiedad ValidationGroup ASP.NET 2.0 incorpora una nueva propiedad, ValidationGroup, con la que se pueden validar datos selectivamente mediante grupos de controles de validación en los formularios de datos que no utilizan los controles preintegrados. Por ejemplo, tal vez no quiera aplicar todos los validadores a una entrada nueva de la tabla Orders. En ese caso, defina un nombre de grupo, como EditGroup1, en la propiedad ValidationGroup de un botón de envío EditGroup1 y los controles de validación de los cuadros de texto y otros controles vinculados del formulario. Otros controles de entrada de datos con validadores en EditGroup2 actualizarán los campos restantes. Aplicar grupos de validación a controles vinculados GridView es más problemático. Los CommandFields autoinsertados no proporcionan acceso directo a sus propiedades, por lo que no se puede especifiar el nombre del ValidationGroup sin añadir un CommandField obligatorio. Si añade una plantilla InsertItem a un control FormView o DetailsView, debe añadir los controles de validación requeridos a las dos plantillas EditItem e InsertItem. El proceso es similar al de agrupar controles de validación en la edición y entrada de datos. 8.3 Otras propiedades de validación compartidas A continuación, ofrecemos una breve descripción de las propiedades de control más importantes compartidas por la mayoría de controles de validación: Y ControlToValidate es un valor necesario de la propiedad ID para todos los controles de validación, excepto CustomValidator. 273 VisualBasic2005_08.qxp 02/08/2007 16:30 PÆgina 274 Bases de datos con Visual Basic Y Y Y Y ErrorMessage aparece junto al control asociado si no se especifica un valor para Text. Especifique siempre un valor para Text y añada el texto que quiere mostrar como ErrorMessage en los cuadros de texto o de mensaje de SummaryValidator. DisplayMode especifica el modo en que el control de validación muestra la propiedad Text. El valor por defecto, Static, guarda espacio para el mensaje bajo o junto al control, según la disponibilidad. Dynamic sólo ocupa espacio en el formulario cuando se muestra un error de validación y suele ser la opción preferida. None impide que se muestre el mensaje, excepto con el control asociado ValidationSummary, si existe. ToolTip: su texto ayuda al usuario ya que permite una descripción larga del error de validación. Por ejemplo, se puede copiar el valor de ErrorMessage en ToolTip y añadir información para ayudar al usuario a corregir el error. EnableClientScript determina si hay validación por parte del cliente o no; el valor por defecto es True. Si se define el valor False, la validación no tendrá lugar hasta que el usuario no envíe el formualrio. A continuación, mostramos un ejemplo del código fuente reformateado para un RequiredFieldValidator: <asp:TemplateField SortExpression= OrderDate HeaderText= Order Date > <EditItemTemplate> <asp:TextBox ID= txtOrderDate Runat= server Width= 76px Text= <%# Bind( OrderDate , {0:d} ) %> ></asp:TextBox> <asp:RequiredFieldValidator ID= rfvOrderDate Runat= server ControlToValidate= txtOrderDate ErrorMessage= OrderDate is required. Display= Dynamic ToolTip= OrderDate is a required field.Required! </asp:RequiredFieldValidator> </EditItemTemplate> <ItemStyle HorizontalAlign= Right VerticalAlign= Top ></ItemStyle> <ItemTemplate> <asp:Label Runat= server Text= <%# Bind( OrderDate , {0:d} ) %> ID= lblOrderDate ></asp:Label> </ItemTemplate> </asp:TemplateField> La siguiente figura muestra el valor Required para la propiedad Text bajo una entrada vacía de OrderDate. 8.4 Validar ediciones en GridView En los siguientes apartados veremos cómo pedir a los usuarios que introduzcan datos en un campo específico, así cómo aplicar expresiones regulares para verificar el formato de los datos, o limitar las entradas a una serie de valores, validar datos comparando valores con los datos de otra columna o un valor calculado, y sacar el mejor partido del control ValidationSummary. Si bien un GridView vinculado a un SqlDataSource proporciona los elementos fijos de un test de control de validación, las técnicas que aprenderá a 274 VisualBasic2005_08.qxp 02/08/2007 16:30 PÆgina 275 Aplicar técnicas avanzadas con ASP.NET 2.0 continuación se aplican en cualquier control editable, independientemente del tipo de fuente de datos. 8.4.1 Añadir un campo necesario de validación a un control GridView A continuación veremos el proceso básico para añadir un control de validación a la plantilla EditItem de un GridView en modo Diseño: 1. Abra la etiqueta inteligente de GridView y pulse Editar plantillas. Pulse con el botón derecho la etiqueta inteligente ItemTemplate y seleccione la opción Editar plantillas y la columna plantilla para su validación. 2. Ajuste la anchura del control TextBox en el panel EditItemTemplate para colocar el texto de entrada, borre el valor por defecto de la propiedad Height y cambie el valor de ID por un nombre descriptivo, txtCustomerID en nuestro ejemplo. 3. Arrastre uno de los controles de validación –en nuestro ejemplo, RequiredFieldValidator– desde la sección Validation del Cuadro de herramientas hasta la derecha del cuadro de texto, el control mostrará el mensaje de error RequiredFieldValidator, en rojo por defecto. 4. Abra la ventana Propiedades del validator, asigne un nombre relacionado –como rfvCustomerID–al valor de la propiedad ID y el nombre del cuadro de texto asocia- 275 VisualBasic2005_08.qxp 02/08/2007 16:30 PÆgina 276 Bases de datos con Visual Basic do a la propiedad ControlToValidate. Cambie el valor Static de la propiedad Display por Dynamic, substituya el ErrorMessage por defecto por una descripción breve de la regla de validación, añada un mensaje corto para que aparezca bajo el cuadro de texto como valor de la propiedad Text y añada un texto opcional para ToolTip, tal como muestra la siguiente figura. Al validar los GridViews, defina el valor Top para la propiedad ItemStyle.VerticalAlign en el cuadro de diálogo Fields. Ese ajuste alineará horizontalmente todos los cuadros de texto cada vez que se muestre un mensaje de error. 8.5 Validar entradas CustomerID con un control RegularExpressionValidator El campo CustomerID de la tabla Order requiere cinco letras mayúsculas, por lo que es un buen candidato para comprobar la validación mediante una expresión regular. La sencilla expresión [A-Z]{5} es suficiente para este test; [A-Z] especifica las letras mayúsculas de la A hasta la Z y {5} indica el número de veces que ha de aparecer la letra en el texto relacionado. Escribir expresiones regulares está más allá del alcance de este libro. El sitio Regular Expressions Library, en la dirección http://www.regexlib.com/, tiene unas 800 expresiones indexadas, con una amplia variedad de formatos de texto estándar y semi-estándar. La entrada de la ayuda online: acerca de las expresines regulares (About Regular Expressions) contiene apartados dónde se explica el funcionamiento de estas expresiones y se describe las clases del espacionombre System.Text.RegularExpressions. 276 VisualBasic2005_08.qxp 02/08/2007 16:31 PÆgina 277 Aplicar técnicas avanzadas con ASP.NET 2.0 Para añadir un RegularExpressionValidator a la plantilla CustomerID del GridView, siga los pasos descritos en el apartado anterior, pero arrastrando un control RegularExpressionValidator hasta la derecha del RequiredFieldValidator. Cambie adecuadamente los valores de las propiedades del RequiredFieldValidator, y escriba [A-Z]{5} como valor de ValidationExpression. Pulse el botón de construcción (builder button) en el cuadro de texto ValidationExpression y se abrirá el cuadro de diálogo Editor de expresiones regulares, con una serie de expresiones prefabricadas para europeos, norteamericanos y asiáticos (ver la siguiente figura). A continuación vemos el código fuente reformado para el TemplateField CustomerID con los controles RequiredValueValidator y RegularExpressionValidator añadidos: <asp:TemplateField SortExpression= CustomerID HeaderText= Cust. ID > <EditItemTemplate> <asp:TextBox ID= txtCustomerID Runat= server Width= 52px Text= <%# Bind( CustomerID ) %> ></asp:TextBox> <asp:RequiredFieldValidator ID= rfvCustomerID Runat= server ErrorMessage= CustomerID is required. Display= Dynamic ControlToValidate= txtCustomerID ToolTip= CustomerID is a required field. > Required! </asp:RequiredFieldValidator>&nbsp; <asp:RegularExpressionValidator ID= revCustomerID Runat= server ErrorMessage= CustomerID must be 5 capital letters. Display= Dynamic ControlToValidate= txtCustomerID ValidationExpression= [A-Z]{5} ToolTip= CustomerID must be 5 capital letters. >[ABCDE] 277 VisualBasic2005_08.qxp 02/08/2007 16:31 PÆgina 278 Bases de datos con Visual Basic </asp:RegularExpressionValidator> </EditItemTemplate> <ItemStyle HorizontalAlign= Left VerticalAlign= Top ></ItemStyle> <ItemTemplate> <asp:Label Runat= server Text= <%# Bind( CustomerID ) %> ID= lblCustomerID ></asp:Label> </ItemTemplate> </asp:TemplateField> El ejemplo anterior ha sido elaborado para demostrar la validación de regex. En las aplicaciones de la vida real, lo mejor es definir los valores de CustomerID en una lista desplegable (DropDownList, parecida a las de las columnas EmployeeID o ShipVia del ValidatedGridView) y comprobarlos contrastando un Custom Validator con una tabla de datos con valores CustomerID válidos. 8.5.1 Comprobar los valores de EmployeeID con un control RangeValidator Los valores de clave foránea de EmployeeID deben ser entre 1 y 9. La lista desplegable ddlEmployee original, que permite a los usuarios seleccionar apellidos en una lista, evita que los usuarios seleccionen un valor EmployeeID no válido. En este ejemplo se ha incluido un item [LastName] no válido en la consulta SQL sobre dsEmployeesSqlDataSource. Veámoslo: SELECT [EmployeeID], [LastName] FROM [Employees] UNION SELECT 0, [Last Name] ORDER BY [LastName] El control RangeValidator requiere que se seleccione un valor apropiado (Integer) para la propiedad Type y se especifiquen valores en las propiedades MinimumValue(1) y MaximumValue(9) para los datos numéricos y de fecha. 8.5.2 Aplicar RangeValidator y RegularExpressionValidator a las entradas de datos Para evitar el acceso al servidor cuando los usuarios entran fechas inexistentes o en el formato erróneo, hay que verificar la entrada con un RangeValidator que acepta fechas dentro de unos ciertos límites. Los límites que se definan dependen de la fuente de datos para ese campo, pero la mayoría de aplicaciones tendrán, probablemente, 1/1/1980 como MinimumValue y 12/31/2099 como MaximumValue. Si se especifica Date como valor de la propiedad Type, el DateTime de .NET comprueba que la fecha sea válida. Como ejemplo, 2/29/2005 o 11/31/00 provocaría un error, pero 2/29/2004 o 02/29/00, no. Por lo tanto, una buena práctica de programación es añadir un RangeValidator a todas las columnas Datetime de los cuadros de texto vinculados. Por defecto, el DateTime acepta años con dos o cuatro dígitos, y barras o guiones como separadores. Si quiere implantar un formato específico más corto, por ejemplo M/D/YYYY, deberá añadir un validador RegularExpression. La siguiente regex requiere vírgulas/comas y años escritos con cuatro dígitos: 278 VisualBasic2005_08.qxp 02/08/2007 16:31 PÆgina 279 Aplicar técnicas avanzadas con ASP.NET 2.0 ^((((0?[13578])|(1[02]))[\/]?((0?[1-9]|[0-2][0-9])|(3[01])))|(((0?[469])|(11))[\/] ?((0?[1-9]|[0-2][0-9])|(30)))|(0?[2][\/]?(0?[1-9]|[0-2][0-9])))[\/]?\d{4}$ La regex anterior es una modificación de una de las expresiones donadas por Cliff Schneide al sitio Regular Expressions Library. Las modificaciones impiden los separadores con guiones de unión y exigen años de cuatro dígitos. Veamos el código fuente reformateado para el OrderDate TemplateField con los controles RequiredFieldValidator, RangeValidator y RegularExpressionValidator: <asp:TemplateField SortExpression= OrderDate HeaderText= Order Date > <EditItemTemplate> <asp:TextBox ID= txtOrderDate Runat= server Width= 76px Text= <%# Bind( OrderDate , {0:d} ) %> ></asp:TextBox> <asp:RequiredFieldValidator ID= rfvOrderDate Runat= server ControlToValidate= txtOrderDate ErrorMessage= OrderDate is required. Display= Dynamic ToolTip= OrderDate is a required field. >Required! </asp:RequiredFieldValidator> <asp:RangeValidator ID= rvOrderDate Runat= server ToolTip= Dates must be in ShortDate format (MM/DD/YYYY) ControlToValidate= txtOrderDate ErrorMessage= Dates must be in M/D/YYYY format. MinimumValue= 1/1/1980 MaximumValue= 12/31/2099 Type= Date Display= Dynamic >[M/D/YYYY] </asp:RangeValidator> <asp:RegularExpressionValidator ID= revOrderDate Runat= server ToolTip= Date format must be M/D/YYYY and date must be valid. Display= Dynamic ErrorMessage= Date format must be M/D/YYYY. ControlToValidate= txtOrderDate ValidationExpression= ^((((0?[13578])|(1[02]))[\/]?((0?[1-9]|[0-2] [0-9])|(3[01])))|(((0?[469])|(11))[\/]?((0?[1-9]|[0-2][0-9])| (30)))|(0?[2]\/?(0?[1-9]|[0-2][0-9])))[\/]?\d{4}$ >[M/D/YYYY] </asp:RegularExpressionValidator> </EditItemTemplate> <ItemStyle HorizontalAlign= Right VerticalAlign= Top ></ItemStyle> <ItemTemplate> <asp:Label Runat= server Text= <%# Bind( OrderDate , {0:d} ) %> ID= lblOrderDate ></asp:Label> </ItemTemplate> </asp:TemplateField> 8.6 Impedir entradas ilógicas con un CompareValidator Se puede utilizar un CompareValidator para impedir las entradas numéricas y las fechas con valores que van contra las reglas de negocio (o el sentido común), por ejemplo un RequiredDate igual o inferior a la fecha OrderDate. La propiedad ControlToCompare del 279 VisualBasic2005_08.qxp 02/08/2007 16:31 PÆgina 280 Bases de datos con Visual Basic control CompareValidator requiere que el ID de un control tenga un tipo de datos compatibles con el del ControlToValidate especificado. Por ejemplo, para comparar un valor Integer con otro que tenga una fracción decimal, hay que especificar Double como valor de Type. En este ejemplo vamos a substituir el control CompareValidator por el RangeValidator. Especifique txtRequiredDate como valor de ControlToValidate, txtOrderDate como ControlToCompare, GreaterThan como Operator y Date como Type. A continuación, el código fuente reformateado para el RequiredDate TemplateField: <asp:TemplateField SortExpression= RequiredDate HeaderText= Required Date > <EditItemTemplate> <asp:TextBox ID= txtRequiredDate Runat= server Width= 76px Text= <%# Bind( RequiredDate , {0:d} ) %> ></asp:TextBox> <asp:RequiredFieldValidator ID= rfvRequiredDate Runat= server ToolTip= RequiredDate is required and must be later than OrderDate. Display= Dynamic ErrorMessage= RequiredDate is required. ControlToValidate= txtRequiredDate >Required! </asp:RequiredFieldValidator> <asp:CompareValidator ID= cvRequiredDate Runat= server ToolTip= RequiredDate must be later than OrderDate. Display= Dynamic ErrorMessage= RequiredDate must be later than OrderDate. ControlToValidate= txtRequiredDate Operator= GreaterThan ControlToCompare= txtOrderDate >Impossible! </asp:CompareValidator> <asp:RegularExpressionValidator ID= revRequiredDate Runat= server ToolTip= Date format must be M/D/YYYY and date must be valid. Display= Dynamic ErrorMessage= Date format must be M/D/YYYY. ControlToValidate= txtRequiredDate ValidationExpression= ^((((0?[13578])|(1[02]))[\/]?((0?[1-9]|[0-2] [0-9])|(3[01])))|(((0?[469])|(11))[\/]?((0?[1-9]|[0-2][0-9])| (30)))|(0?[2]\/?(0?[1-9]|[0-2][0-9])))[\/]?\d{4}$ >[M/D/YYYY] </asp:RegularExpressionValidator> </EditItemTemplate> <ItemStyle HorizontalAlign= Right VerticalAlign= Top ></ItemStyle> <ItemTemplate> <asp:Label Runat= server Text= <%# Bind( RequiredDate , {0:d} ) %> ID= lblRequiredDate ></asp:Label> </ItemTemplate> </asp:TemplateField> 8.6.1 Añadir un control CustomValidator Los controles CustomValidator requieren añadir un manejador de validación por parte del servidor, para el evento ValidatorName_ServerValidate y una función opcional JScript 280 VisualBasic2005_08.qxp 02/08/2007 16:31 PÆgina 281 Aplicar técnicas avanzadas con ASP.NET 2.0 o VBScript para la validación por parte del cliente. Este ejemplo valida las ediciones de la columna Freight y requiere una entrada de 5 o más si la columna ShippedDate contiene una fecha. El validador utiliza la "política comercial de envío mínimo de $ 5.00 y cargo adicional" de los comerciantes de Northwind. Veamos el código fuente reformateado para el Freight TemplateField, que especifica el manejador de evento cvFreight_ServerValidate por parte del servidor, y el valor de la propiedad ClientValidationFunction de VBScript por parte del cliente: <asp:TemplateField SortExpression= Freight HeaderText= Freight > <EditItemTemplate> <asp:TextBox ID= txtFreight Runat= server Width= 52px Text= <%# Bind( Freight ) %> ></asp:TextBox> <asp:RequiredFieldValidator ID= rfvFreight Runat= server ErrorMessage= Freight is required; enter 0 if not known. Display= Dynamic ControlToValidate= txtFreight >Required! </asp:RequiredFieldValidator> <asp:CustomValidator ID= cvFreight Runat= server ToolTip= Freight for shipped order cannot be less than $5.00 ControlToValidate= txtFreight Display= Dynamic ErrorMessage= Freight for shipped order is less than $5.00 OnServerValidate= cvFreight_ServerValidate ClientValidationFunction= ValidateFreight > <5=Bad! </asp:CustomValidator> </EditItemTemplate> <ItemStyle HorizontalAlign= Right VerticalAlign= Top ></ItemStyle> <ItemTemplate> <asp:Label Runat= server Text= <%# Bind( Freight , {0:C2} ) %> ID= lblFreight ></asp:Label> </ItemTemplate> </asp:TemplateField> El siguiente manejador de evento cvFreight_ServerValidate contiene código para obtener el valor de otra columna GridView de la fila editada. El código también define el valor False para la propiedad args.IsValid siempre que haya una fecha en la columna ShippedDate y el valor de Freight sea menor que $5.00. Sub cvFreight_ServerValidate(ByVal source As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs) args.IsValid = True If Val(args.Value) < 5 Then With gvOrdersEditable Dim gvrRow As GridViewRow = .Rows(.EditIndex) Dim txtShipped As TextBox = CType(gvrRow.FindControl("txtShippedDate"), TextBox) If txtShipped IsNot Nothing Then If Len(txtShipped.Text) > 4 Then 281 VisualBasic2005_08.qxp 02/08/2007 16:31 PÆgina 282 Bases de datos con Visual Basic args.IsValid = False End If End If End With End If End Sub Escribir el código para la función de validación por parte del cliente es un poco más complicado. Hay que inferir el nombre del campo a verificar del valor de Document.activeElement.id, el cual devuelve gvOrdersEditable_ctl14_txtFreight del siguiente elemento <input> activo: <input name= gvOrdersEditable$ctl14$txtFreight type= text value= 0 id= gvOrdersEditable_ctl14_txtFreight style= width:52px; /> Substituya txtFreight por txtShippedDate para crear el valor del atributo id para la misma fila y aplique el método Document.getElementById(ShipDateId).outerHTML para devolver el elemento ShippedDate <input>: <input name= gvOrdersEditable$ctl14$txtShippedDate type= text value= 5/10/1998 id= gvOrdersEditable_ctl14_txtShippedDate style= width:76px; /> Finalmente, extraiga el texto del atributo value para determinar si existe algún valor ShippedDate. A continuación, vemos la función VBScript en la sección <head> que implementa la validación por parte del cliente: <script language= vbscript > Function ValidateFreight(source, args) If args.Value < 5 Then FreightID = Document.activeElement.id ShipDateID = Left(FreightID, InStrRev(FreightID, _ )) & txtShippedDate ShipDate = Document.getElementById(ShipDateID).outerHTML ShipDate = Mid(ShipDate, Instr(ShipDate, value= ) + 6) ShipDate = Left(ShipDate, Instr(ShipDate, name= ) -1) If Len(ShipDate) > 4 Then args.IsValid = False Else args.IsValid = True End If End If End Function </script> Puede escribir código similar para realizar cálculos de validación de fechas, como substituir el control CompareValidator por el RequiredDate con un CustomValidator que requiera un mínimo de siete días de diferencia entre los valores de OrderDate RequiredDate. 282