Creación de Capa de Acceso a Datos ASP
Creación de Capa de Acceso a Datos ASP
INTRODUCCIN ........................................................................................................... 5 1. 2. 3. CREACIN DE UNA CAPA DE ACCESO A DATOS ..................................................................7 CREACIN DE UNA CAPA LGICA DE NEGOCIOS ..............................................................45 PAGINAS MAESTRAS Y NAVEGACIN DEL SITIO ................................................................64
INFORMES BSICOS.................................................................................................... 89 4. 5. 6. VISUALIZACIN DE DATOS CON EL OBJECTDATASOURCE .............................................90 DECLARACIN DE PARAMETROS ..........................................................................................107 DEFINICIN MEDIANTE PROGRAMACIN DE LOS VALORES DE LOS PARAMETROS
DEL OBJECTDATASOURCE...............................................................................................................117 MAESTRO / DETALLE ............................................................................................... 126 7. 8. 9. 10. FILTRADO MAESTRO/DETALLE CON UN DROPDOWNLIST ............................................127 FILTRADO MAESTRO/DETALLE CON DOS DROPDOWNLISTS .......................................138 FILTRADO MAESTRO/DETALLE A TRAVS DE DOS PGINAS ........................................154 FILTRADO MAESTRO/DETALLE USANDO UN GRIDVIEW MAESTRO
SELECCIONABLE CON DETALLES DETAILVIEW ..........................................................................170 FORMATO PERSONALIZADO ..................................................................................... 181 11. 12. 13. 14. 15. FORMATO PERSONALIZADO BASADO EN LOS DATOS................................................182 USO DE PLANTILLAS DE CAMPO EN EL CONTROL GRIDVIEW ...................................202 USANDO PLANTILLAS DE CAMPO EN EL CONTROL DETAILSVIEW ..........................222 USO DE LAS PLANTILLAS EN LOS FORMVIEWS ..............................................................235 MOSTRAR INFORMACIN RESUMIDA EN EL PIE DE PGINA DEL GRIDVIEW .........242
EDICIN, INSERCIN Y ELIMINACIN DE DATOS....................................................... 257 16. UN RESUMEN DE LA EDICIN, INSERCIN Y ELIMINACIN DE DATOS .................258
17.
Y ELIMINACIN. .................................................................................................................................296 18. 19. MANEJO DE EXCEPCIONES EN UNA PGINA [Link] A NIVEL DE DAL Y BLL ......320 AGREGAR CONTROLES DE VALIDACION PARA LAS INTERFACES DE EDICION E
INSERCIN ...........................................................................................................................................336 20. 21. 22. 23. PERSONALIZACION DE LA INTERFAZ DE MODIFICACION DE DATOS .....................356 APLICACIN DE LA CONCURRENCIA OPTIMISTA .........................................................375 ADICION DE CONFIRMACION DE ELIMINACION DEL LADO DEL CLIENTE .............414 LIMITACIN DE LA FUNCION DE MODIFICACION DE DATOS BASADOS EN EL
USUARIO...............................................................................................................................................424 PAGINACION Y ORDENAMIENTO DE DATOS.............................................................. 444 24. 25. 26. 27. PAGINACIN Y ORDENAMIENTO DE DATOS DEL INFORME ......................................445 PAGINACIN EFICIENTE A TRAVS DE LARGAS CANTIDADES DE DATOS ............465 ORDENAMIENTO PERSONALIZADO DE DATOS PAGINADOS .....................................492 CREAR UNA INTERFAZ DE USUARIO DE ORDENAMIENTO PERSONALIZADO........505
ACCIONES DE BOTONES PERSONALIZADAS ............................................................... 519 28. AGREGAR Y RESPONDER A LOS BOTONES DEL GRIDVIEW .........................................520
MOSTRANDO DATOS CON EL DATALIST Y EL REPEATER ........................................... 544 29. 30. MOSTRANDO DATOS CON LOS CONTROLES DATALIST Y REPEATER ....................545 DAR FORMATO AL DATALIST Y REPEATER BASADOS EN LOS DATOS ...................568
31. MOSTRAR VARIOS REGISTROS POR FILA CON EL CONTROL DATALIST.....................581 32. CONTROLES DE DATOS WEB ANIDADOS ............................................................................588 ESCENARIOS DE FILTRADO CON EL DATALIST Y REPEATER ....................................... 601 33. FILTRADO MAESTRO/ DETALLE CON UN DROPDOWNLIST ...........................................602 34. FILTRADO MAESTRO/DETALLE A TRAVES DE DOS PGINAS ........................................615
35. FILTRADO MAESTRO/DETALLE USANDO UNA LISTA CON VIETAS DE LOS REGISTROS MAESTROS Y UN DATALIST DE DETALLES ...........................................................629 EDICION Y ELIMINACION DE DATOS CON EL DATALIST ............................................. 650 36. RESUMEN DE EDICIN Y ELIMINACIN DE DATOS EN EL DATALIST ..........................651 37. REALIZACION DE ACTUALIZACIONES POR LOTES ............................................................674 38. MANEJO DE EXCEPCIONES A NIVEL DE DAL Y BLL ...........................................................686 39. AGREGAR CONTROLES DE VALIDACIN A LA INTERFAZ DE EDICIN DEL DATALIST ................................................................................................................................................................695 40. PERSONALIZAR LA INTERFAZ DE EDICION DEL DATALIST .............................................707 PAGINACION Y ORDENACION CON EL DATALIST Y REPEATER ................................... 722 41. PAGINAR UN REPORTE DE DATOS CON UN CONTROL DATALIST Y REPEATER .......723 42. ORDENAR DATOS EN UN CONTROL DATALIST Y REPEATER.........................................743 PERSONALIZAR ACCIONES DE BOTON CON EL DATALIST Y REPEATER ....................... 775 43. PERSONALIZAR ACCIONES DE BOTON CON EL DATALIST Y REPEATER .....................776 TRABAJAR CON ARCHIVOS BINARIOS........................................................................ 787 51. CARGA DE ARCHIVOS ...............................................................................................................788 52. MOSTRAR DATOS BINARIOS EN LOS CONTROLES DE DATOS WEB .............................808 53. INCLUIR LA OPCIN DE CARGA DE ARCHIVO CUANDO AGREGAMOS UN NUEVO REGISTRO.............................................................................................................................................824 54. ACTUALIZACIN Y ELIMINACIN DE DATOS BINARIOS EXISTENTES .........................845 ALMACENAMIENTO DE DATOS EN CACHE ................................................................ 873 55. ALMACENAMIENTO DE DATOS EN CACHE CON EL OBJECTDATASOURCE ................874 56. ALMACENAMIENTO DE DATOS EN CACHE EN LA ARQUITECTURA .............................876 57. ALMACENAMIENTO DE DATOS EN CACHE AL INICIO DE LA APLICACIN ................877 58. UTILIZACIN DE SQL PARA DEPENDENCIAS DE CACHE .................................................878 MANEJO DE MAPAS DEL SITIO DE BASE DE DATOS .................................................... 879
59. CONSTRUCCIN DE UN PROVEEDOR PERSONALIZADO DE MAPA DE SITIO QUE MANEJE LA BASE DE DATOS ...........................................................................................................880 TRABAJAR CON LOTES DE DATOS ............................................................................ 881 60. ENVOLVER MODIFICACIONES DE BASE DE DATOS EN UNA TRANSACIN ................882 61. ACTUALIZACIN DE LOTES ....................................................................................................883 62. ELIMINACIN POR LOTES ........................................................................................................884 63. INSERCIN POR LOTES .............................................................................................................885 ESCENARIOS AVANZADOS DE ACCESO A DATOS ...................................................... 886 64. CREACIN DE PROCEDIMIENTOS ALMACENADOS PARA LOS TABLEADAPTERS DEL DATASET TIPIADO .............................................................................................................................887 65. UTILIZAR PROCEDIMIENTOS ALMACENADOS EXISTENTES PARA LOS TABLEADAPTERS DEL DATASET TIPIADO ...................................................................................919 66. ACTUALIZAR EL TABLEADAPTER PARA UTILIZAR JOINs .................................................938 67. AGREGAR COLUMNAS ADICIONALES AL DATATABLE .....................................................956 68. TRABAJAR CON COLUMNAS CALCULADAS ........................................................................971 69. CONFIGURACION DE LA CONEXION DE LA CAPA DE ACCESO A DATOS Y DEFINICIONES A NIVEL DE COMANDO ........................................................................................990 70. PROTECCIN DE LAS CADENAS DE CONEXIN Y OTRA INFORMACIN DE CONFIGURACIN ............................................................................................................................ 1002 71. DEPURACIN DE PROCEDIMIENTOS ALMACENADOS ................................................... 1017 72. CREACIN DE PROCEDIMIENTOS ALMACENADOS Y FUNCIONES DEFINIDAS POR EL USUARIO CON MANAGED CODE ................................................................................................ 1019
INTRODUCCIN
Figura 1. Crear un nuevo archivo de sitio web basado en el sistema. Esto crear un nuevo sitio web con una pgina [Link] [Link], una carpeta App_Data y un archivo [Link]. Con el sitio web creado, el siguiente paso es agregar una referencia a la base de datos en el explorador de servidores de Visual Studio. Agregando una base de datos al explorador de servidores podemos agregar sus tablas, procedimientos almacenados, vistas, entre otros; todo desde Visual Studio. Tambin puede ver los datos de las tablas o crear sus propias consultas a mano o grficamente por medio del Generador de Consultas. Por otra parte, cuando construimos el DataSet con tipo para la DAL necesitamos un punto desde Visual Studio hacia la base de datos desde la cual el DataSet con tipo sera construido. Aunque en este punto, podemos proporcionar la informacin de esta conexin, Visual Studio poblar automticamente la lista desplegable de las bases de datos registradas en el explorador de servidores. Los pasos para agregar la base de datos Northwind al explorador de servidores, depende de si usted desea usar la base de datos de SQL Server 2005 Express Edition en la carpeta App_Data o si tiene una configuracin del servidor de base de datos Microsoft SQL Server 2000 o 2005 que desee usar en su lugar. Usar una base de datos en la carpeta App_Data
Si no tiene un servidor de base de datos SQL Server 2000 o 2005 al cual conectarse, o simplemente desea evitar agregar la base de datos a un servidor de base de datos, puede utilizar la versin SQL Server 2005 Express Edition de la base de datos Northwind que est localizado en el sitio web de descarga de la carpeta App_Data ([Link]). Una base de datos situada en la carpeta App_Data se agrega automticamente al explorador de servidores. Asumiendo que tiene SQL Server 2005 Express Edition instalado en su computador, debera ver en el explorador de soluciones un nodo llamado [Link] que puede expandirse y explorar sus tablas, vistas, procedimientos almacenados y as sucesivamente. (Ver figura 2). La carpeta App_Data tambin puede contener archivos Microsoft Access .mdb, que al igual que sus homlogos de SQL Server, se agregaran automticamente al Explorador de Servidores. Conexin a la base de datos desde un servidor SQL Server 2000 o 2005 Alternativamente se puede conectar una base de datos Northwind instalada en un servidor de base de datos. Si el servidor de base de datos no tiene instalada la base de datos Northwind, primero debemos agregarla al servidor de base de datos. Una vez que haya instalado la base de datos, vaya al explorador de servidores en Visual Studio, haga clic en el nodo conexiones de datos y elija Agregar conexin. Si no ve el explorador de servidores, vaya a Ver/Explorador de Servidores o presione Ctrl+Alt+S. Esto abrir el cuadro de dialogo Agregar Conexin, en el que se puede especificar el servidor al cual desea conectarse, la informacin de autenticacin y el nombre de la base de datos. Una vez haya configurado correctamente la informacin de la conexin de la base de datos y haga clic en el botn Aceptar, la base de datos se agrega como un nodo debajo del nodo Conexiones de datos. Puede expandir el nodo de la base de datos para explorar sus tablas, vistas, procedimientos almacenados y as sucesivamente. Paso 2: Creacin de la capa de acceso a datos Cuando trabajamos con datos una opcin es integrar la lgica especfica de datos directamente dentro de la capa de presentacin (en una pgina web, las pginas [Link] representan la capa de presentacin). Estas pueden tomar la forma de escritura
de cdigo [Link] en la parte de cdigo de la pgina [Link] o utilizando un control SqlDataSource en la porcin de marcado. En cualquier caso este enfoque une la lgica de acceso a datos con la capa de presentacin. Sin embargo lo que se recomienda es separar la lgica de acceso a datos de la capa de presentacin. Esta capa independiente se conoce capa de acceso a datos, para abreviar DAL y normalmente es implementada como un proyecto separado de biblioteca de clases. Los beneficios de esta arquitectura estn bien documentados y este es el enfoque que tendr esta serie de tutoriales.
Figura 2. Agregar una conexin de la base de datos Northwind a su servidor de base de datos. Todo el cdigo que es especifico del origen de datos subyacentes, as como la creacin de una conexin a la base de datos, la emisin de comandos SELECT, INSERT, UPDATE y DELETE, deben ser ubicados en la DAL. La capa de presentacin no debe contener ninguna referencia a dicho cdigo de acceso a datos, sino que debe realizar llamadas a la DAL para cualquiera de todos los datos solicitados. Las capas de acceso a datos normalmente contienen mtodos para acceder a la base de datos subyacente. Las tablas de la base de datos Northwind, por ejemplo Products y Categories registran los productos para la venta y la categora a la que pertenecen. En nuestra DAL tendremos mtodos como: GetCategories ( ), devuelve la informacin de todas las categoras. GetProducts ( ), devuelve la informacin de todos los productos.
GetProductsByCategoryId ( ), devuelve todos los productos que pertenecen a una categora GetProductByProductId (), devuelve la informacin acerca de un producto
Cuando estos mtodos son invocados, se conectan a la base de datos, ejecutan la consulta apropiada y devuelven los resultados. Lo importante es cmo devuelven los resultados? Estos mtodos solo pueden devolver un DataSet o un DataReader poblado con la consulta de la base de datos, pero lo ideal es que estos resultados sean devueltos usando objetos fuertemente tipiados. Un objeto fuertemente tipiado es aquel cuyo esquema esta rgidamente definido en tiempo de compilacin, mientras que por el contrario un objeto dbilmente tipiado es aquel cuyo esquema no se conoce sino en tiempo de ejecucin. El DataSet y el DataReader (por defecto) son objetos dbilmente tipiados, ya que su esquema est definido por las columnas devueltas por la consulta de base de datos utilizada para poblarlos. Para acceder a una columna en particular de un DataTable dbilmente tipiado se utiliza sintaxis como: [Link] (index) (ColumnName). En los DataTable dbilmente tipiados de este ejemplo est expuesto el hecho que para acceder al nombre de una columna necesitamos usar una cadena o un ndice ordinal. Por el contrario un DataTable fuertemente tipiado tendr cada una de sus columnas implementadas como propiedades, lo que resulta en un cdigo parecido a este: [Link] (ndex).ColumnName. Para devolver objetos fuertemente tipiados, los desarrolladores pueden crear sus objetos de negocio personalizados o usar DataSets con tipos. Un objeto de negocio es implementado por el desarrollador como una clase cuyas propiedades normalmente reflejan las columnas de la tabla de la base de datos subyacente que el objeto de negocio representa. Un DataSet con tipo es una clase generada por usted para Visual Studio basado en un esquema de base de datos y cuyos miembros son fuertemente tipiados de acuerdo a este esquema. El DataSet con Tipo en si mismo consiste de clases que comprenden las clases DataSet, DataTable y DataRow de [Link]. Adems de los DataTables fuertemente tipiados, los DataSet tipiados ahora tambin incluyen TableAdapters, los cuales son clases con mtodos para poblar los DataTables del DataSet y propagar las modificaciones en los DataTables de regreso a la base de datos.
Nota: Para obtener ms informacin de las ventajas y desventajas de usar DataSets tipiados versus los objetos de negocio personalizados, consulte Diseo de componentes de niveles de datos y anlisis de los datos a travs de los niveles. Usaremos los DataSet fuertemente tipiados para la arquitectura de estos tutoriales. La figura 3 muestra el diagrama de flujo entre las diferentes capas de una aplicacin que utiliza DataSets tipiados.
Fig. 3 Todo el cdigo de acceso a datos es relegado a la DAL Creacin de un DataSet tipiado y un TableAdapter Para empezar a crear nuestra DAL, empezamos agregando un DataSet tipiado a nuestro proyecto. Para realizar esto hacemos clic derecho en el nodo del proyecto en el explorador de soluciones y seleccionamos agregar nuevo elemento. Seleccionamos la opcin DataSet de la lista de plantillas y la llamamos [Link].
Fig. 4 Seleccionamos agregar un nuevo DataSet a su proyecto Despus de hacer clic en agregar y cuando se nos solicite agregar el DataSet a la carpeta App_Code, seleccionamos S. Entonces se mostrara el diseador para el DataSet
y se iniciar el asistente de configuracin del TableAdapter, permitindole agregar el primer TableAdapter a su DataSet tipiado. Un DataSet con tipo sirve como una coleccin de datos fuertemente tipiados, este est compuesto de instancias DataTable fuertemente tipiadas, cada una de las cuales est compuesta de instancias DataRow fuertemente tipiadas. Comenzaremos creando un DataTable fuertemente tipiado para cada una de las tablas de la base de datos subyacente que necesitamos para trabajar en esta serie de tutoriales. Comenzaremos creando un DataTable para la tabla Products. Tenga en cuenta que los DataTable fuertemente tipiados no incluyen ninguna informacin sobre como acceder a los datos de la tabla de la base de datos subyacente. Con el fin de recuperar los datos para poblar el DataTable, usaremos una clase TableAdapter la cual funciona como nuestra capa de acceso a datos. Para nuestra DataTable Products, el TableAdapter contendr los mtodos GetProducts (), GetProductsByCategoryID (categoryID) y otros que se invocaran desde la capa de presentacin. El papel de los DataTables es el de servir como objetos para pasar datos entre las capas. El asistente de configuracin del TableAdapter comienza pidindole que seleccione la base de datos con la cual trabajar. La lista desplegable muestra todas las bases de datos en el explorador de servidores. Si no ha agregado la base de datos Northwind al explorador de servidores, en este momento puede hacerlo, haciendo clic en el botn Nueva Conexin. Despus de seleccionar la base de datos y hacer clic en Siguiente, se nos preguntara si deseamos guardar la cadena de conexin en el archivo [Link]. Al guardar la cadena de conexin evitaremos tenerla codificada en las clases TableAdapter, lo cual simplifica las cosas si por alguna razn la informacin de la cadena de conexin cambia en el futuro. Si usted selecciona guardar la cadena de conexin en el archivo de configuracin, esta es guardada en la seccin <connectionStrings>, que opcionalmente puede ser encriptada para mayor seguridad o modificaciones posteriores por medio de las nuevas propiedades de la pgina [Link] en la herramienta de administracin de interfaz grafica de usuario IIS, que es ms ideal para administradores.
Luego necesitamos definir el esquema para el primer DataTable fuertemente tipiado y proporcionar el primer mtodo a nuestro TableAdapter que usara cuando poblemos el DataSet fuertemente tipiado. Estos dos pasos se completan de forma simultnea por medio de la creacin de una consulta que devuelve las columnas desde la tabla que deseamos reflejar en nuestro DataTable. Al finalizar el asistente le daremos un nombre al mtodo de esta consulta. Una vez que realicemos esto, el mtodo ser invocado desde nuestra capa de presentacin. El mtodo ejecutara la consulta definida y poblara un DataTable fuertemente tipiado.
Figura 6. Guardar la cadena de conexin en [Link] Para empezar a definir la consulta, primero debemos indicar como queremos que el TableAdapter ejecute la consulta. Podemos usar una sentencia ad-hoc, crear un nuevo procedimiento almacenado o utilizar un procedimiento almacenado ya existente. Para estos tutoriales utilizaremos las instrucciones SQL. Consulte el articulo de Bryan Noyes Construir una capa de acceso a datos con el diseador del DataSet de Visual Studio 2005 para un ejemplo del uso de procedimientos almacenados.
Figura 7. Consultar los datos usando una sentencia SQL ad-hoc En este punto podemos escribir la consulta SQL a mano. Cuando creamos el primer mtodo en el TableAdapter normalmente queremos tener la consulta que devuelva las columnas que necesitan ser expresadas en el DataTable correspondiente. Para realizar esto, creamos una consulta que devuelve todas las columnas y las filas de la tabla Products:
Figura 8. Ingrese el cdigo de consulta SQL en el cuadro de texto Tambin puede utilizar el generador de consultas y construir grficamente la consulta como se muestra en la Figura 9.
Figura 9. Crear la consulta de forma grfica, por medio del Generador de Consultas
Despus de crear la consulta, pero antes de movernos a la siguiente pantalla, damos clic en el botn Opciones avanzadas. En el proyecto del sitio web Generar sentencias Insert, Update, Delete es la nica opcin avanzada seleccionada por defecto, si ejecutamos el asistente desde una biblioteca de clases o desde un proyecto de Windows, la opcin Usar concurrencia optimista tambin estara seleccionada. Por ahora deje la opcin Usar concurrencia optimista sin marcar. Examinaremos la concurrencia optimista en futuros tutoriales.
Figura 10. Seleccione solo la opcin Generar sentencias Insert, Update y Delete Despus de verificar las opciones avanzadas, damos clic en siguiente para pasar a la pantalla final. Aqu se nos pide seleccionar los mtodos para agregar al TableAdapter. Existen dos modelos para poblar los datos: Llenar un objeto DataTable, con este enfoque se crea un mtodo que tomar el DataTable como un parmetro y lo poblar basado en los resultados de la consulta. La clase DataAdapter de [Link], por ejemplo, implementa este enfoque con su mtodo Fill (). Devolver un DataTable, con este enfoque el mtodo crea y llena el DataTable por usted y lo devuelve como el valor de retorno del mtodo. Usted puede tener un TableAdapter con uno o ambos enfoques aplicados. Tambin puede cambiar el nombre de los mtodos establecidos aqu, dejaremos ambas casillas de verificacin seleccionadas, aunque solo usaremos el ltimo enfoque a travs de todos estos tutoriales. Adems cambiaremos el nombre genrico GetData por GetProducts.
Si verificamos, la casilla final GenerateDBDirectMethods crea los mtodos Insert (), Update () y Delete () del TableAdapter. Si deja esta opcin sin seleccionar, todas las actualizaciones necesitan hacerse por medio del mtodo exclusivo Update () del TableAdapter, las cuales se hacen en un DataSet tipiado, un DataTable, un DataRow o un conjunto de DataRows. (Si usted no selecciona la opcin Generar sentencias Insert, Update y Delete en las propiedades avanzadas de la figura 9, esta casilla de verificacin no tendr ningn efecto). Dejaremos seleccionada esta casilla.
Figura 11. Cambiar el nombre del mtodo GetData por GetProducts Complete el asistente haciendo clic en Finalizar. Despus el asistente se cierra y volveremos al Diseador de DataSet el cual le muestra el DataTable que acabamos de crear. Usted puede ver la lista de columnas en el DataTable Products (ProductID, ProductName, etc.) as como los mtodos de la ProductsTableAdapter (Fill () y GetProducts ()).
Figura 12. Cambiar el nombre del mtodo GetData por GetProducts Hasta el momento tenemos y una un DataSet clase tipiado con un nico DataTable tipiada
([Link])
DataAdapter
fuertemente
([Link]) con un mtodo GetProducts (). Estos objetos pueden ser usados para acceder a una lista de todos los productos desde un cdigo as:
Dim productsAdapter As New [Link] () Dim products as [Link] products= [Link]() For Each productRow As [Link] In products [Link] ("Product: " & [Link] & "<br />")
Este cdigo no requiere que escribamos ni un poco de cdigo de acceso a datos especfico. No tuvimos que crear ninguna instancia de clases [Link], ni tuvimos que referirnos a ninguna cadena de conexin, consultas SQL o procedimientos almacenados. En su lugar, el TableAdapter gener el cdigo de acceso a datos de bajo nivel por nosotros. Cada objeto usado en este ejemplo tambin es fuertemente tipiado, permitindole a Visual Studio proporcionar Intellisense y la comprobacin en tiempo de compilacin. Lo mejor de todo es que los DataTable devueltos por el TableAdapter pueden ser enlazados a controles de datos web [Link], como el GridView, DetailsView,
DropDownList, CheckBoxList y otros. El siguiente ejemplo muestra el enlace del DataTable devuelto por el mtodo GetProducts () a un GridView en tan solo tres lneas de cdigo en el controlador de eventos del evento Page_Load. [Link]
<%@ Page Language="VB" AutoEventWireup="true" CodeFile="[Link]" Inherits="AllProducts" %> <! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link] <html xmlns="[Link] > <head runat="server"> <title>View All Products in a GridView</title> <link href="[Link]" rel="stylesheet" type="text/css" /> </head> <body> <form id="form1" runat="server"> <div> <h1> All Products</h1> <p> <asp:GridView ID="GridView1" runat="server" CssClass="DataWebControlStyle"> <HeaderStyle CssClass="HeaderStyle" /> <AlternatingRowStyle CssClass="AlternatingRowStyle" /> </asp:GridView> </p> </div> </form> </body> </html>
[Link]
Imports NorthwindTableAdapters Partial Class AllProducts Inherits [Link] Protected Sub Page_Load(ByVal sender As Object, ByVal e As [Link]) _ Handles [Link] Dim productsAdapter As New ProductsTableAdapter
Figura 13. La lista de productos es mostrada en un GridView Mientras este ejemplo requiere que escribamos tres lneas de cdigo en el controlador de eventos Page_Load de la pgina [Link], en los futuros tutoriales examinaremos como usar el ObjectDataSource para la declaracin de la recuperacin de datos desde la DAL. Con el ObjectDataSource no tenemos que escribir ningn cdigo y tambin obtendremos soporte de paginacin y agrupacin. Paso 3: Adicin de mtodos parametrizados a nuestra capa de acceso a datos Hasta este momento nuestra clase ProductsTableAdapter tiene un solo mtodo, el cual devuelve todos los productos en la base de datos. Si bien la posibilidad de trabajar con todos los productos es til, hay ocasiones en que deseemos recuperar informacin de un producto especfico, o de todos los productos que pertenecen a una categora especifica. Para agregar esta funcionalidad a nuestra capa de acceso a datos podemos aadir mtodos parametrizados al TableAdapter. Aadiremos el mtodo GetProductsByCategoryID (categoryID). Para agregar un nuevo mtodo, regresamos al diseador de DataSet, hacemos clic en la seccin TableAdapter y seleccionamos Agregar Consulta.
Figura 14. Haga clic en el TableAdapter y elija Agregar Consulta Lo primero que aparece es si deseamos acceder a la base de datos usando una instruccin SQL ad-hoc o por medio de un nuevo procedimiento almacenado o uno ya existente. Optamos por seleccionar de nuevo la sentencia ad-hoc. Luego nos preguntan qu tipo de consulta deseamos utilizar. Ya que deseamos devolver todos los productos pertenecientes a una categora en particular, deseamos escribir una sentencia SELECT que devuelva filas. El siguiente paso es definir la consulta SQL usada para acceder a los datos. Como solo deseamos devolver aquellos productos que pertenecen a una categora especfica, usamos la misma declaracin de GetProducts () pero agregamos la siguiente clausula WHERE CategoryID=@CategoryID. El parmetro @CategoryID indica al asistente del TableAdapter que el mtodo que estamos creando requiere un parmetro de entrada del tipo correspondiente (es decir, un entero que acepta valores Null).
Figura 15. Seleccione crear una sentencia SELECT que devuelve filas
Figura 16. Escriba una consulta para devolver solo los productos que pertenezcan a una categora especfica. En el ltimo paso podemos elegir los enfoques de acceso a datos que usaremos, as como personalizar los nombres de los mtodos generados. Para el enfoque de relleno, cambiaremos el nombre a FillByCategoryID y para el enfoque de devolucin de un DataTable (los mtodos Get X) usaremos GetProductsByCategoryID.
Figura 17. Seleccione los nombres para los mtodos del TableAdapter Despus de completar el asistente, el diseador del DataSet incluye nuevos mtodos al TableAdapter.
Figura 18. Los productos pueden ser consultados por categora Tome un momento para agregar un mtodo GetProductByProductID (productID) usando la misma tcnica. Estas consultas parametrizadas pueden ser probadas directamente desde el diseador de DataSet. De clic derecho en el mtodo en el TableAdapter y seleccione Vista previa de datos. Luego ingrese los valores para usar por los parmetros y de clic en Vista Previa.
Figura 19. Los productos pertenecientes a la categora Bebidas son mostrados Con el mtodo GetProductByProductID (productID) en nuestra DAL, podemos crear una pgina [Link] que muestre solo los productos de una categora especfica. El siguiente ejemplo muestra todos los productos que estn en la categora Bebidas, que tiene un CategoryID de 1.
Figura 20. Los productos de la categora Bebidas son mostrados Paso 4: Actualizar, insertar y eliminar datos
Hay dos enfoques de uso general para insertar, actualizar y eliminar datos. El primer enfoque el cual llamaremos enfoque directo de base de datos, implica la creacin de mtodos que luego son invocados, generando un comando INSERT, DELETE y UPDATE a la base de datos operando en un solo registro de la base de datos. Estos valores suelen pasarse en una serie de valores escalares (enteros, cadenas, bolanos, fechas, horas y as sucesivamente) correspondientes a los valores a insertar, actualizar o eliminar. Por ejemplo con este enfoque para la tabla Products, el mtodo delete tomara un parmetro entero, indicando el ProductID del registro que se desea eliminar, mientras que el mtodo Insert tomara una cadena para el ProductName, un decimal para el UnitPrice, un nmero entero para UnitsOnStock y as sucesivamente.
Figura 21. Cada solicitud Insert, Update y Delete es enviada inmediatamente a la base de datos El otro enfoque, al cual nos referiremos como enfoque de actualizacin por lotes, es para actualizar un DataSet completo, un DataTable o una coleccin de DataRows en una llamada al mtodo. Con este enfoque un desarrollador elimina, inserta y actualiza los DataRows en un DataTable y luego pasa estos DataRows o DataTable a un mtodo de actualizacin. Luego este mtodo enumera los DataRows pasados y determina si ha sido o no modificado, agregado o borrado (por medio del valor de la propiedad RowState del DataRow) y emite la solicitud apropiada a la base de datos para cada registro.
Figura 22. Todos los cambios son sincronizados con la base de datos cuando el mtodo Update es invocado El TableAdapter por defecto siempre utiliza el enfoque de actualizacin por lotes, pero tambin soporta el enfoque directo de base de datos. Ya que seleccionamos la opcin Generar sentencias Update, Insert y Delete en las propiedades avanzadas cuando creamos nuestro TableAdapter, el TableAdapter Products contiene un mtodo Update (), el cual implementa el enfoque de actualizacin por lotes. En concreto el TableAdapter contiene un mtodo Update () que puede ser pasado al DataSet tipiado, un DataTable fuertemente tipiado o uno o ms DataRows. Si usted dejo seleccionada la casilla GenerateDBDirectMethods cuando creamos el primer TableAdapter, el enfoque directo de base de datos tambin ser implementado por medio de los mtodos Update (), Delete () y Insert (). Ambos enfoques de modificacin de datos utilizan las propiedades InsertCommand, UpdateCommand y DeleteCommand del TableAdapter para generar sus comandos Insert, Update y Delete a la base de datos. Podemos inspeccionar y modificar las propiedades InsertCommand, UpdateCommand y DeleteCommand haciendo clic en el TableAdapter en el diseador del DataSet y luego ir a la ventana propiedades. (Asegrese propiedades). de tener seleccionada el TableAdapter y que el objeto ProductsTableAdapter es el nico seleccionado en la lista desplegable de la ventana de
Figura 23. El TableAdapter tiene propiedades InsertCommand, UpdateCommand y DeleteCommand Para examinar o modificar cualquiera de estas propiedades de comando de la base de datos, haga clic en la sub-propiedad CommandText, lo cual abrir el Generador de Consultas. El siguiente cdigo muestra cmo utilizar el enfoque de actualizacin por lotes para doblar el precio de todos los productos que no estn descontinuados y que tienen 25 unidades de existencia o menos:
Dim productsAdapter As New [Link] () Dim products As [Link] = [Link] () For Each product As [Link] In products If Not product. Discontinued AndAlso [Link] <= 25 Then [Link] *= 2 End if Next [Link] (products)
Figura 24. Configurar las sentencias Insert, Update y Delete en el generador de Consultas El siguiente cdigo muestra cmo usar el enfoque directo de base de datos para que mediante programacin eliminemos un producto en particular, luego actualicemos uno y finalmente agreguemos un nuevo producto:
Dim productsAdapter As New [Link] () [Link](3) [Link]( _ "Chai", 1, 1, "10 boxes x 20 bags", 18.0, 39, 15, 10, false, 1) [Link]( _ "New Product", 1, 1, "12 tins per carton", 14.95, 15, 0, 10, false)
Creacin de mtodos Insert, Update y Delete personalizados Los mtodos Insert (), Update () y Delete (), creados por el enfoque directo de base de datos pueden ser un poco engorrosos, especialmente para tablas con muchas columnas. Observando el ejemplo anterior sin la ayuda de Intellisense no es claro que columnas de la tabla Products se unen a cada parmetro de entrada de los mtodos Insert () y Update (). Pueden haber ocasiones en que solo deseemos actualizar una o dos columnas, o deseemos un mtodo personalizado Insert () que tal vez devuelva el valor del campo IDENTITY (incremento automtico) del registro recin creado.
Para crear un mtodo personalizado, regrese al diseador de DataSet, haga clic en el TableAdapter y elija Agregar nueva consulta, regresando al asistente del TableAdapter. En la segunda pantalla podemos indicar el tipo de consulta que deseamos crear. Crearemos un mtodo que agregue un nuevo registro y luego devuelva el valor del ProductID del registro recin creado. Por lo tanto la opcin es crear una consulta INSERT. En la siguiente pantalla aparece el CommandText del InsertCommand. Aumentaremos esta consulta agregando al final de la consulta SELECT SCOPE IDENTITY (), el cual devolver el valor de la ultima identidad insertado en la columna IDENTITY en el mismo alcance. (Consulte la documentacin tcnica para obtener ms informacin acerca de SCOPE IDENTITY () y es probable que desee usarlo en lugar de @ @IDENTITY.). Asegrese de finalizar la instruccin INSERT con un punto y coma antes de agregar la sentencia SELECT.
Figura 25. Crear un mtodo para agregar una nueva fila a la tabla Products
Figura 26. Aumentar la consulta para devolver el valor SCOPE IDENTITY () Por ltimo nombre el nuevo mtodo InsertProduct.
Figura 27. Establezca el nombre del nuevo mtodo en InsertProduct Cuando regrese al diseador del DataSet vera que el TableAdapter Products contiene un nuevo mtodo InsertProduct. Si este nuevo mtodo no tiene un parmetro de entrada para cada columna de la tabla Products, lo ms probable es que haya olvidado
finalizar la declaracin INSERT con un punto y coma. Configure el mtodo InsertProduct y asegrese de que existe un punto y coma delimitando las sentencias INSERT y SELECT. Por defecto los mtodos Insert generan mtodos de No-consulta, lo que significa que devolver el nmero de filas afectadas. Sin embargo, queremos que el mtodo devuelva el valor regresado por la consulta y no el nmero de filas afectadas. Para realizar esto, modificamos la propiedad ExecuteMode del mtodo InsertProduct a Scalar.
Figura 28. Cambiar la propiedad ExecuteMode a Scalar El siguiente cdigo muestra el nuevo mtodo InsertProduct en accin:
Dim productsAdapter As New [Link]() Dim new_productID As Integer = Convert.ToInt32([Link]( _ "New Product", 1, 1, "12 tins per carton", 14.95, 10, 0, 10, false)) [Link](new_productID)
Paso 5: Completar la capa de acceso a datos Tenga en cuenta que la clase ProductsTableAdapter devuelve los valores de categoryID y SupplierID de la tabla Products, pero no devuelve la columna CategoryName de la tabla Categories o la columna CompanyName de la tabla Suppliers, aunque es probable que deseemos mostrar estas columnas cuando mostremos la informacin de los productos. Podemos aumentar el mtodo inicial GetProducts () del TableAdapter para
incluir los valores de las columnas CategoryName y CompanyName los cuales actualizaran el DataTable fuertemente tipiado para incluir estas columnas tambin. Esto podra presentar un problema, ya que los mtodos para insertar, actualizar y eliminar datos estn basados en este mtodo inicial. Afortunadamente los mtodos automticamente generados para insertar, actualizar y eliminar no son afectados por las sub-consultas de la sentencia SELECT. Al tomar la precaucin de agregar nuestras consultas Categories y Suppliers como sub-consultas, en lugar de JOINs, evitaremos tener que elaborar nuevamente los mtodos de modificacin de datos. Haga clic en el mtodo GetProducts () del ProductsTableAdapter y elija Configurar. Luego ajuste la sentencia SELECT para que se vea de la siguiente forma:
SELECT ProductID, ProductName, SupplierID, CategoryID,QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT (SELECT FROM Products CategoryName CompanyName FROM FROM Suppliers WHERE Categories WHERE = [Link]=[Link]) as CategoryName, [Link] [Link]) as SupplierName
Figura 29. Actualizar la sentencia SELECT para el mtodo GetProducts () Despus de actualizar el mtodo GetProducts () para usar esta nueva consulta el DataTable incluir dos nuevas columnas: CategoryName y SupplierName.
Figura 30. El DataTable Products tiene dos nuevas columnas Tmese un momento para actualizar la sentencia SELECT en el mtodo
GetProductsByCategoryID (categoryID). Si actualiza el SELECT GetProducts () usando sentencias JOINs, el diseador del DataSet no ser capaz de generar automticamente los mtodos para insertar, actualizar y eliminar datos de la base de datos usando el enfoque directo de base de datos. En su lugar tendr que crearlos manualmente al igual que hicimos anteriormente con el mtodo InsertProduct en este tutorial. Adems de la forma manual, tendr que proporcionar los valores de las propiedades InsertCommand, UpdateCommand y DeleteCommand si desea utilizar el enfoque de actualizacin por lotes. Agregar los TableAdapters restantes Hasta el momento solo hemos visto el trabajo con un solo TableAdapter para una sola tabla de la base de datos. Sin embargo la base de datos Northwind contiene varias tablas relacionadas con las cuales tendremos que trabajar en nuestra aplicacin web. Un DataSet tipiado puede contener muchos DataTables relacionados. Por lo tanto para completar nuestra DAL necesitamos agregar DataTables para las otras tablas que usaremos en estos tutoriales. Para agregar un nuevo TableAdapter al DataSet tipiado, abra el diseador del DataSet, haga clic derecho en el diseador y seleccione Agregar/ TableAdapter. Esto creara un nuevo objeto DataTable y TableAdapter e iremos a travs del asistente que vimos anteriormente en este tutorial.
Tmese unos minutos para crear los TableAdapters y los mtodos usando las siguientes consultas. Tenga en cuenta que las consultas en el TableAdapter incluyen las sub-consultas para agregar la categora de cada producto y el nombre del proveedor. Adems si usted ya ha continuado, ya ha agregado la clase TableAdapter Products y los mtodos GetProducts () y GetProductsByCategoryID (categoryID).
Products TableAdapter
GetProducts:
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,UnitPrice, UnistInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE [Link]=[Link]) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE [Link]=[Link]) as SupplierName FROM Products
GetProductsByCategoryID:
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE [Link] = [Link]) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE [Link] = [Link]) as SupplierName FROM Products WHERE CategoryID = @CategoryID
GetProductsBySupplierID:
ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice,
SELECT
UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryNameFROM Categories WHERE [Link] =[Link]) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE [Link] = [Link]) as SupplierName FROM Products WHERE SupplierID = @SupplierID
GetProductsByProductID:
ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice,
SELECT
UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE [Link] =[Link]) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE [Link] = [Link]) as SupplierName FROM Products WHERE ProductID = @ProductID
Categories TableAdapter
GetCategories:
GetCategoryByCategoryID:
Suppliers TableAdapter
GetSuppliers:
GetSupplersByCountry:
SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers WHERE Country = @Country
GetSupplierBySupplierID:
Employees TableAdapter
GetEmployees:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees
GetEmployesByManager:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE ReportsTo = @ManagerID
GetEmployeeByEmployeeID:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE EmployeeID = @EmployeeID
Figura 31. El DataSet despus que se han agregado los cuatro DataTables Agregar cdigo personalizado a la DAL Los TableAdapters y los DataTables agregados al DataSet tipiado son expresados como un archivo XML de definicin de Esquemas ([Link]). Usted puede ver esta informacin de esquema haciendo clic derecho sobre [Link] en el explorador de soluciones y seleccionando Ver cdigo. Esta informacin de esquema se traduce en cdigo Visual Basic o C# en tiempo de diseo o cuando se compila en tiempo de ejecucin (si es necesario), momento en el cual usted puede caminar a travs de l con el depurador. Para ver el cdigo generado automticamente vaya a la vista de clases y llegue hasta las clases TableAdapter o DataSet tipiado. Si usted no ve la vista de clases en la pantalla, vaya a men, ver y seleccinelo all, o presionando Ctrl+Shift+C. Desde la vista de clases usted puede ver las propiedades, mtodos y eventos de las clases TableAdapters y DataSets tipiados. Para ver el cdigo de un mtodo especifico, haga doble clic sobre el nombre del mtodo en la Vista de Clases o haga clic derecho sobre l y seleccione Ir a definicin.
Aunque el cdigo generado automticamente puede ser un gran ahorro de tiempo, a menudo el cdigo es muy genrico y debe ser personalizado para satisfacer necesidades nicas de una aplicacin. El riesgo de extender un cdigo generado automticamente, es que la herramienta que genero el cdigo podra decidir que es hora de reconstruir y sobrescribir en las personalizaciones. Con el nuevo concepto de clase parcial de .Net 2.0, es fcil dividir una clase en varios archivos. Esto nos permite aadir nuestros propios mtodos, propiedades y eventos para las clases generadas automticamente sin tener que preocuparnos de que Visual Studio sobrescriba en nuestras personalizaciones.
Figura 32. El archivo XML de definicin de esquemas (XSD) para el DataSet tipiado Northwind Para demostrar como personalizar nuestra DAL, vamos a agregar nuestro mtodo GetProducts () a la clase SuppliersRow. La clase SuppliersRow representa un nico registro de la tabla Suppliers, cada proveedor puede tener de cero a muchos productos, por lo cual GetProducts () devolver todos los productos de un proveedor especifico. Para realizar esto, creamos un nuevo archivo de clase en la carpeta App_Code llamado [Link] y agregamos el siguiente cdigo:
Imports NorthwindTableAdapters Partial Public Class Northwind Partial Public Class SuppliersRow
Public Function GetProducts() As [Link] Dim productsAdapter As New ProductsTableAdapter Return [Link]([Link]) End Function End Class End Class
Esta
clase
parcial
indica
al
compilador
que
cuando
construya
la
clase
[Link] incluya el mtodo GetProducts() que acabamos de definir. Si usted construye su proyecto y luego vuelve a la vista de clases, puede encontrar que el mtodo GetProducts() aparece ahora como un mtodo de [Link]. El mtodo GetProducts() ahora puede ser usado para enumerar el conjunto de productos de un proveedor en particular, como se muestra en el siguiente cdigo:
Dim suppliersAdapter As New [Link]() Dim suppliers As [Link] = [Link]() For Each supplier As [Link] In suppliers [Link]("Supplier: " & [Link]) [Link]("<ul>") Dim products As [Link] = [Link]() For Each product As [Link] In products [Link]("<li>" & [Link] & "</li>") Next [Link]("</ul><p> </p>") Next
Figura 33. Inspeccione el cdigo generado automticamente seleccionado Ir a definicin desde la vista de clases Estos datos tambin pueden ser mostrados en cualquiera de los controles de datos web de [Link]. La siguiente pgina muestra un control GridView con dos campos:
Un BoundField que muestra el nombre de cada proveedor, y Un TemplateField que contiene un control BulletedList que esta enlazado a los resultados devueltos por el mtodo GetProducts () para cada proveedor.
Figura 34. El mtodo GetProducts () ahora es parte de la clase [Link] Veremos ms adelante como mostrar informes maestro/detalles en futuros tutoriales. Por ahora este ejemplo sirve para ilustrar el uso de un mtodo personalizado agregado a la clase [Link].
[Link]
<%@ Page Language="VB" CodeFile="[Link]" AutoEventWireup="true" Inherits="SuppliersAndProducts" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link] <html xmlns="[Link] > <head runat="server"> <title>Untitled Page</title> <link href="[Link]" rel="stylesheet" type="text/css" /> </head> <body> <form id="form1" runat="server"> <div> <h1> Suppliers and Their Products</h1> <p> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CssClass="DataWebControlStyle"> <HeaderStyle CssClass="HeaderStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" /> <Columns> <asp:BoundField DataField="CompanyName" HeaderText="Supplier" /> <asp:TemplateField HeaderText="Products"> <ItemTemplate> <asp:BulletedList ID="BulletedList1" runat="server" DataSource="<%#CType(CType([Link], [Link]).Row,[Link]) .GetProducts() %>" DataTextField="ProductName"> </asp:BulletedList> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> </p> </div> </form> </body> </html>
[Link]
Imports NorthwindTableAdapters Partial Class SuppliersAndProducts Inherits [Link] Protected Sub Page_Load(ByVal sender As Object, ByVal e As [Link]) _ Handles [Link] Dim suppliersAdapter As New SuppliersTableAdapter [Link] = [Link]() [Link]() End Sub End Class
Figura 35. El nombre de la compaa proveedora es listado en la columna izquierda y sus productos en la derecha RESUMEN Al construir una aplicacin web, la creacin de la DAL debe ser uno de los primeros pasos, que se producen antes de empezar la capa de presentacin. Con Visual Studio la creacin de una DAL basada en DataSets tipiados es una tarea que puede llevarse a cabo en 10 o 15 minutos sin escribir una sola lnea de cdigo. Los siguientes tutoriales se basaran en esta DAL. En el siguiente tutorial vamos a definir una serie de reglas de negocio y la forma de ponerlas en prctica en una capa separada de lgica de negocios.
Figura 1. La BLL separa la capa de presentacin de la capa de acceso a datos e impone las reglas de negocio En lugar de crear clases separadas para implementar nuestra lgica de negocios BLL, podramos colocar alternativamente esta lgica directamente en un DataSet tipiado con clases parciales. Para un ejemplo de creacin y ampliacin de un DataSet tipiado refirase de nuevo al tutorial anterior. Paso 1: Creacin de clases BLL Nuestra BLL estar compuesta de cuatro clases, una para cada TableAdapter en la DAL, cada una de estas clases tendr mtodos para recuperar, insertar, actualizar y eliminar desde el respectivo TableAdapter en la DAL, aplicando las reglas de negocio adecuadas. Para separar ms limpiamente la DAL y las clases BLL relacionadas, crearemos dos subcarpetas DAL y BLL en nuestra carpeta App_Code. Simplemente haga clic en la carpeta App_Code en el explorador de soluciones y seleccione Nueva Carpeta. Despus de crear estas dos subcarpetas, mueva el DataSet tipiado creado en el primer tutorial a la subcarpeta DAL.
A continuacin cree cuatro archivos de clase en la subcarpeta BLL. Para realizar esto, haga clic derecho en la subcarpeta BLL y seleccione agregar nuevo elemento y escoja plantilla de clase. Nombre las cuatro clases ProductsBLL, CategoriesBLL, SuppliersBLL y EmployeesBLL.
Figura 2. Anadir cuatro clases a la carpeta App_Code Luego agregamos mtodos a cada una de las clases para que sencillamente envuelvan los mtodos definidos para los TableAdapters en el primer tutorial. Por ahora estos mtodos llamaran directamente en la DAL, regresaremos despus para agregar la lgica de negocios necesaria. Nota: Si est utilizando Visual Studio Standard Edition o superior (es decir no est utilizando Visual Web Developer), si lo desea puede disear sus clases de forma visual utilizando el diseador de clases. Consultemos el blog diseador de clases para obtener ms informacin sobre esta nueva funcin de Visual Studio. Para la clase ProductsBLL tenemos que aadir un total de siete mtodos:
Get Products (), devuelve todos los productos. GetProductsByProductId (productID), devuelve el producto con el ProductID
especificado.
[Link]
Imports NorthwindTableAdapters <[Link]()> _ Public Class ProductsBLL Private _productsAdapter As ProductsTableAdapter = Nothing Protected ReadOnly Property Adapter() As ProductsTableAdapter Get If _productsAdapter Is Nothing Then _productsAdapter = New ProductsTableAdapter() End If Return _productsAdapter End Get End Property <[Link] _ ([Link], True)> _ Public Function GetProducts() As [Link] Return [Link]() End Function <[Link] _ ([Link], False)> _ Public Function GetProductByProductID(ByVal productID As Integer) _ As [Link] Return [Link](productID) End Function <[Link] _ ([Link], False)> _
Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _ As [Link] Return [Link](categoryID) End Function <[Link] _ ([Link], False)> _ Public Function GetProductsBySupplierID(ByVal supplierID As Integer) _ As [Link] Return [Link](supplierID) End Function <[Link] _ ([Link], True)> _ Public Function AddProduct( _ productName As String, supplierID As Nullable(Of Integer), _ categoryID As Nullable(Of Integer), quantityPerUnit As String, _ unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _ unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _ discontinued As Boolean) _ As Boolean Dim products As New [Link]() Dim product As [Link] = [Link]() [Link] = productName If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If quantityPerUnit Is Nothing Then [Link]()
Else [Link] = quantityPerUnit End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If [Link] = discontinued [Link](product) Dim rowsAffected As Integer = [Link](products) Return rowsAffected = 1 End Function <[Link] _ ([Link], True)> _ Public Function UpdateProduct(_ productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _ unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _ unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _ discontinued As Boolean, productID As Integer) _ As Boolean Dim products As [Link] = _ [Link](productID) If [Link] = 0 Then Return False End If Dim product as [Link] = products(0) [Link] = productName If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If quantityPerUnit Is Nothing Then [Link]() Else [Link] = quantityPerUnit End If If Not [Link] Then [Link]() Else [Link] = [Link] End If
If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If [Link] = discontinued Dim rowsAffected As Integer = [Link](product) Return rowsAffected = 1 End Function <[Link] _ ([Link], True)> _ Public Function DeleteProduct(ByVal productID As Integer) As Boolean Dim rowsAffected As Integer = [Link](productID) Return rowsAffected = 1 End Function End Class
Los
mtodos
que
solo
devuelven
datos
GetProducts,
GetProductByProductID,
GetProductsByCategoryID y GetProductsBySupplierID son bastante sencillos ya que solamente llaman a la DAL. Aunque en algunos escenarios podra haber reglas de negocio que necesitan ser implementadas a este nivel (como las reglas de actualizacin basadas en el usuario actual o el rol al cual pertenece el usuario) simplemente dejaremos estos mtodos como estn. Para estos mtodos, la BLL solo sirve como una aproximacin a travs de la cual la capa de presentacin tiene acceso a los datos subyacentes de la base de datos.
Los mtodos AddProduct y UpdateProduct toman como parmetros los valores para los diversos campos del producto y agregan un nuevo producto o actualizan uno existente, respectivamente. Como muchas de las columnas de la tabla Products pueden aceptar valores nulos (CategoryID, SupplierID y UnitPrice, por nombrar algunos) estos parmetros de entrada para AddProduct y UpdateProduct se asignan a dichas columnas que usan tipos anulables. Los tipos anulables son nuevos en .Net 2.0 y proporcionan una tcnica para saber si un tipo de valor podra ser Nothing. Consulte el blog de Vick Paul La verdad acerca de los tipos anulables y VB y a la documentacin tcnica para la estructura anulable para mayor informacin. Los otros tres mtodos devuelven un valor booleano indicando si una fila fue insertada, actualizada o eliminada ya que la operacin podra no resultar en una fila afectada. Por ejemplo si el desarrollador llama a DeleteProduct pasando un ProductID para un producto que no existe, la sentencia DELETE emitida a la base de datos no tendr ningn efecto y por lo tanto el mtodo DeleteProduct devolver False. Tenga en cuenta que al agregar un nuevo producto o actualizar uno existente tomamos los valores de los campos del nuevo o modificado producto como una lista de escalares en lugar de aceptar una instancia ProductsRow. Este enfoque fue seleccionado, debido a que la clase ProductsRows deriva de la clase DataRows de [Link] la cual no tiene por defecto un constructor sin parmetros. Por lo tanto para crear una nueva instancia ProductsRow, primero debemos crear una instancia ProductsDataTable y luego invocamos su mtodo newProductRow (lo cual hacemos en AddProduct). Esta deficiencia sobresale cuando procedemos a insertar y actualizar productos utilizando los ObjectDataSource. En resumen el ObjectDataSource tratar de crear una instancia de los parmetros de entrada. Si el mtodo BLL espera una instancia ProductsRow, el ObjectDataSource tratara de crear una, pero fallara debido a la falta de un constructor predeterminado sin parmetros. Para obtener mayor informacin refirase a los siguientes dos foros: Actualizacin de ObjectDataSource con DataSets tipiados fuertemente y Problemas con los ObjectDataSource y los DataSet fuertemente tipiados. Luego en AddProduct y UpdateProduct, el cdigo crea una instancia ProductsRow y la rellena con los valores que acabamos de pasar. Cuando asignamos valores a las DataColumn de los DataRows varias validaciones a nivel de campo pueden ocurrir. Por lo tanto poner manualmente los valores pasados de nuevo en un DataRow lo que ayuda
a garantizar la validez de los datos que se pasan al mtodo BLL. Lamentablemente las clases DataRow fuertemente tipiadas generadas por Visual Studio no utilizan los tipos anulables. Por el contrario, para indicar que un valor de un Object DataColumn en un DataRow corresponder a un valor de base de datos Null, debemos usar el mtodo Set ColumnName Null (). En UpdateProducts primero cargamos el producto a actualizar usando
GetProductsByproductID (productID). Si bien esto puede parecer un viaje innecesario a la base de datos, este viaje extra vale la pena explorarlo en futuros tutoriales que exploran la concurrencia optimista. La concurrencia optimista es una tcnica para asegurar que dos usuarios que estn trabajando simultneamente sobre los mismos datos no sobrescriban accidentalmente uno de los cambios del otro. Tomar el registro completo hace ms fcil crear mtodos de actualizacin que solo modifican un subconjunto de las columnas del DataRow. Cuando exploremos la clase SuppliersBLL veremos un ejemplo. Finalmente tenga en cuenta que la clase ProductsBLL tiene el DataObjectAttribute aplicado a esta (la sintaxis [[Link]] antes de la sentencia de clase creada a la parte superior del archivo) y los mtodos tienen atributos DataObjectMethodAttribute. El atributo DataObject marca la clase como un objeto adecuado para el enlace a un control ObjectDataSource, mientras que el DataObjectMethodAttribute indica el propsito del mtodo. Como veremos en futuros tutoriales el ObjectDataSource de [Link] 2.0 hace ms fcil declarar acceso a datos desde una clase. Para ayudar a filtrar la lista de las posibles clases para enlazar en el asistente del ObjectDataSource, por defecto solo las clases marcadas como DataObjects se muestran en la lista desplegable del asistente. La clase ProductsBLL funcionara igual de bien sin estos atributos, pero aadirlos hace que sea ms fcil trabajar con el asistente del ObjectDataSource. Agregar las otras clases Con la clase ProductsBLL completa, aun necesitamos agregar las clases para trabajar con las categoras, empleados y proveedores. Tome un momento para crear las siguientes clases y mtodos usando los conceptos en el ejemplo anterior: [Link] GetCategories()
GetCategoryByCategoryID (categoryID) [Link] GetSuppliers() GetSupplierBySupplierID (supplierID) GetSuppliersByCountry (country) UpdateSupplierAddress (supplierID, adress, city, country) [Link] GetEmployees() GetEmployeesByEmployeeID (employeeID) GetEmployeesByManager (managerID) El mtodo que vale la pena destacar es el mtodo UpdateSupplierAddress de la clase SuppliersBLL. Este mtodo ofrece una interfaz para actualizar solamente la informacin de la direccin del proveedor. Internamente este mtodo lee el objeto SupplierRow para el SupplierID especificado (usando GetSupplierBySupplierID), establece las propiedades relacionadas con la direccin y luego llama al mtodo Update del SupplierDataTable. El mtodo UpdateSupplierAddress es el siguiente:
<[Link].DataObjectMethodAttribute_ ([Link], True)>_ Public Function UpdateSupplierAddress(ByVal supplierID As Integer,_ ByVal address As String, ByVal city As String, ByVal country As String)_ As Boolean Dim suppliers As [Link] = _ [Link](supplierID) If [Link] = 0 Then Return False Else Dim supplier As [Link] = suppliers(0) If address Is Nothing Then [Link]() Else [Link] = address End If
If city Is Nothing Then [Link]() Else [Link] = city End If If country Is Nothing Then [Link]() Else [Link] = country End If Dim rowsAffected As Integer = [Link](supplier) Return rowsAffected = 1 End If End Function
Refirase a la descarga de este artculo para la implementacin completa de las clases BLL. Paso 2: Acceda a los DataSets tipiados a travs de las clases BLL En el primer tutorial vimos ejemplos de cmo trabajar directamente con los DataSets tipiados mediante programacin, pero con la adicin de las clases BLL, la capa de presentacin debe trabajar en su lugar con la BLL. En el ejemplo [Link] del primer tutorial, el ProductsTableAdapter fue utilizado para enlazar la lista de productos a un GridView, como se muestra en el siguiente cdigo:
Dim productsAdapter As New ProductsTableAdapter() [Link] = [Link]() [Link]()
Para usar las nuevas clases BLL, todo lo que necesitamos cambiar es la primera lnea del cdigo remplazando simplemente el objeto ProductsTableAdapter con el objeto ProductsBLL:
Dim productLogic As New ProductsBLL() [Link] = [Link]() [Link]()
Las clases BLL tambin pueden ser accedidas mediante declaracin (al igual que los DataSet tipiados) por medio del ObjectDataSource. Discutiremos el ObjectDataSource con ms detalles en los futuros tutoriales.
Figura 3. La lista de productos es mostrada en un GridView Paso 3: Agregar validacin a nivel de campo a las clases DataRow La verificacin a nivel de campo son comprobaciones a los valores de las propiedades de los objetos de negocio al insertar a actualizar. Algunas reglas de validacin a nivel de campo para los productos incluyen: El campo ProductName debe ser de 40 o menos caracteres de longitud. El campo QuantityPerUnit debe ser de 20 o menos caracteres de longitud. Los campos ProductID, ProductName y Discontinued son obligatorios, pero todos los dems son opcionales. Los campos UnitPrice, UnitsInStock, UnitsOnOrder y ReorderLevel deben ser iguales o mayores a cero. Estas reglas pueden y deben ser expresadas a nivel de base de datos. El lmite de caracteres en los campos ProductName y QuantityPerUnit son capturados por los tipos de datos de estas columnas en la tabla Products (nvarchar(40) y nvarchar(20)
respectivamente).Si los campos son obligatorios o opcionales son expresados si la columna de la tabla de la base de datos permite NULLs. Existen cuatro restricciones de comprobacin para asegurar que solo valores iguales o mayores a cero sean ingresados en las columnas UnitPrice, UnitsInStock, UnitsOnOrder o ReorderLevel. Adems de forzar estas reglas en la base de datos tambin deben aplicarse a nivel de DataSet. De hecho la longitud del campo y si un valor es obligatorio u opcional ya estn capturados para el conjunto de DataColumns de cada DataTable. Para ver la verificacin a nivel de campo proporcionada automticamente, vaya al diseador de DataSet, seleccione un campo de uno de los DataTables y luego vaya a la ventana propiedades. Como se muestra en la figura 4, el DataColumn QuantityPerUnit en el ProductsDataTable tiene una longitud mxima de 20 caracteres y permite valores NULLs. Si se intenta establecer la propiedad QuantityPerUnit del ProductsDataRow a un valor de cadena de ms de 20 caracteres de longitud se emitir un ArgumentException.
Figura 4. El DataColumn proporciona validacin bsica a nivel de campo Desafortunadamente no podemos especificar los controles de lmites, como que el valor UnitPrice debe ser mayor o igual a cero por medio de la ventana propiedades. Con el fin de proporcionar este tipo de validacin a nivel de campo necesitamos crear un controlador de eventos al evento ColumnChanging del DataTable. Como se menciono en el tutorial anterior los objetos DataSet, DataTable y DataRow creados por el DataSet tipiado puede ser extendido empleando clases parciales. Con esta tcnica podemos
crear un controlador de eventos ColumnChanging para la clase ProductsDataTable. Comenzaremos creando una clase en la carpeta App_Code llamada [Link].
Figura 5. Agregar una nueva clase a la carpeta App_Code Luego cree un controlador de eventos para el evento ColumnChanging que asegure que los valores de las columnas UnitPrice, UnitsInStock, UnitsOnOrder y ReorderLevel (si no son NULLs) sean mayores o iguales a cero. Si cualquier columna esta fuera de este rango se emitir un ArgumentException.
[Link]
Imports [Link] Partial Public Class Northwind Partial Public Class ProductsDataTable Public Overrides Sub BeginInit() AddHandler [Link], AddressOf ValidateColumn End Sub Sub ValidateColumn(sender As Object, e As DataColumnChangeEventArgs) If [Link]([Link]) Then If Not [Link]([Link]) AndAlso _ CType([Link], Decimal) < 0 Then Throw New ArgumentException( _ "UnitPrice cannot be less than zero", "UnitPrice")
End If ElseIf [Link]([Link]) OrElse _ [Link]([Link]) OrElse _ [Link]([Link]) Then If Not [Link]([Link]) AndAlso _ CType([Link], Short) < 0 Then Throw New ArgumentException([Link]( _ "{0} cannot be less than zero", [Link]), _ [Link]) End If End If End Sub End Class End Class
Paso 4: Agregar reglas de negocio personalizadas a las clases BLL Adems de la validacin a nivel de campo, pueden haber reglas de negocio personalizadas de alto nivel que involucran diferentes entidades o conceptos no expresivos a nivel de una sola columna, tales como: Si un producto es descontinuado, su UnitPrice no se puede actualizar El pas de residencia de un empleado debe ser el mismo pas de residencia de su jefe Un producto no puede ser descontinuado si es el nico producto proporcionado por un proveedor Las clases BLL deben tener verificaciones para garantizar el cumplimiento de las reglas de negocio de la aplicacin. Estos controles pueden ser agregados directamente a los mtodos que los aplicaran. Imagine que nuestras reglas de negocio determinan que un producto no puede ser descontinuado si es el nico producto suministrado por el proveedor. Es decir si un producto X es el nico producto que obtenemos del proveedor Y, no podemos marcar X como descontinuado, sin embargo si el proveedor Y nos suministra tres productos A, B y C entonces podramos marcar todos y cada uno de estos productos como descontinuado. Es una regla de negocio extraa, pero las reglas de negocio y el sentido comn no siempre estn ligados!
Para
estableciendo si Discontinued se estableci en s, de ser as llamara al mtodo GetProductsBySupplierID para determinar el nmero de productos comprados a este proveedor de productos. Si este es el nico producto que se compra a este proveedor se emitir una ApplicationException.
<[Link].DataObjectMethodAttribute_ ([Link], True)> _ Public Function UpdateProduct( _ productName As String, supplierID As Nullable(Of Integer), _ categoryID As Nullable(Of Integer), quantityPerUnit As String, _ unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _ unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _ discontinued As Boolean, productID As Integer) _ As Boolean Dim products As [Link] = _ [Link](productID) If [Link] = 0 Then Return False End If Dim product As [Link] = products(0) If discontinued Then Dim productsBySupplier As [Link] = _ [Link]([Link]) If [Link] = 1 Then Throw New ApplicationException( _ "You cannot mark a product as discontinued if it is " & _ "the only product purchased from a supplier") End If End If [Link] = productName
If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If quantityPerUnit Is Nothing Then [Link]() Else [Link] = quantityPerUnit End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If If Not [Link] Then [Link]() Else [Link] = [Link] End If [Link] = discontinued Dim rowsAffected As Integer = [Link](product) Return rowsAffected = 1
End Function
Respuesta a los errores de validacin en la capa de presentacin Al llamar a la BLL desde la capa de presentacin podemos decidir si manejar las excepciones que puedan ocurrir o dejarlas a [Link] (Lo que aumentara el evento Error del HttpAplication). Para manejar una excepcin cuando se trabaja con la BLL mediante programacin, podemos usar los bloques TryCatch, como se muestra en el siguiente ejemplo:
Dim productLogic As New ProductsBLL() Try [Link]("Scotts Tea", 1, 1, Nothing, _ -14, 10, Nothing, Nothing, False, 1) Catch ae As ArgumentException [Link]("There was a problem: " & [Link]) End Try
En los futuros tutoriales, exploraremos el controlador de excepciones generado desde la BLL cuando se emplea un control de datos web para insertar, eliminar o actualizar datos pueden ser manipuladas directamente en un controlador de eventos en lugar de tener que envolver el cdigo en bloques Try Catch. RESUMEN Una aplicacin bien diseada se hace a mano en distintas capas, cada una de ellas encierra un papel muy especial. En el primer tutorial de esta serie de artculos hemos creado una capa de acceso a datos usando DataSets tipiados, en este tutorial construiremos una capa lgica de negocios como una serie de clases en la carpeta App_Code de nuestra aplicacin que llame a nuestra DAL. La BLL implementala lgica a nivel de campo y a nivel de negocios de nuestra aplicacin. Adems de la opcin de crear una BLL separada como lo hicimos en este tutorial, otra opcin es ampliar los mtodos de los TableAdapters mediante el uso de clases parciales. Sin embargo, utilizar esta tcnica no nos permite remplazar los mtodos existentes ni separar tan limpiamente nuestra DAL y BLL como lo hemos hecho con el enfoque adoptado en este artculo.
Figura 1. El resultado final de este tutorial Paso 1: Creacin de la pgina maestra El primer paso es crear la pgina principal del sitio. En este momento nuestro sitio solo se compone del DataSet tipiado ([Link] en la carpeta App_Code), las clases BLL ([Link], [Link] y as sucesivamente en la carpeta App_Code), el archivo de configuracin ([Link]) y un archivo de hoja de estilos ([Link]). Limpie las pginas que usamos en los tutoriales anteriores para las demostraciones de la DAL y la BLL ya que examinaremos estos ejemplos con mayor detalle en los futuros tutoriales.
Figura 2. Los archivos de nuestro proyecto Para crear una pgina principal, hacemos clic derecho sobre el nombre del proyecto en el explorador de soluciones y elija agregar un nuevo elemento. Luego, seleccione el tipo pgina maestra de la lista de plantillas y nmbrela [Link].
Figura 3. Agregar una pgina principal a la pgina web Defina el diseo de las pginas de todo el sitio en la pgina maestra. Puede utilizar la vista diseo y agregar los controles web que necesite, o puede agregar el marcado manualmente en la vista cdigo fuente. En mi pgina maestra se utilizan hojas de estilo para el posicionamiento y estilos, con las configuraciones CSS definidas en el archivo externo [Link]. Si bien no podemos decir desde el marcado a partir de donde mostrar, las reglas CSS se definen de tal forma que el contenido de navegacin de los <div>s es una posicin absoluta que aparece a la izquierda y tiene un ancho fijo de 200 pixeles.
[Link]
<%@ Master Language="VB" AutoEventWireup="true" CodeFile="[Link]" Inherits="Site" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
<html xmlns="[Link] > <head runat="server"> <title>Working with Data Tutorials</title> <link href="[Link]" rel="stylesheet" type="text/css" /> </head> <body> <div id="wrapper"> <form id="form1" runat="server"> <div id="header"> <span class="title">Working with Data Tutorials</span> <span class="breadcrumb"> TODO: Breadcrumb will go here...</span> </div> <div id="content"> <asp:contentplaceholder id="MainContent" runat="server"> <!-- Page-specific content will go here... --> </asp:contentplaceholder> </div> <div id="navigation"> TODO: Menu will go here... </div> </form> </div> </body> </html>
Una pgina principal define tanto el diseo esttico de las pginas como las regiones que pueden ser editadas por las pginas [Link] que utilicen la pagina principal. Estas regiones de contenido editable son indicadas por el control ContentPlaceHolder, el cual podemos ver dentro del contenido del <div>. Nuestra pgina principal tiene un solo ContentPlaceHolder ContentPlaceHolders. (MainContent), pero la pgina puede contener varios
Con el marcado de arriba, si cambiamos a vista de diseo nos muestra el diseo de la pgina maestra. Cualquiera de las pginas [Link] que utilicen esta pgina maestra, tendrn este diseo uniforme, con la posibilidad de especificar el marcado de la regin MainContent.
Figura 4. La pgina principal a travs de la vista diseo. Paso 2: Agregar una pgina web al sitio web Con la pagina principal definida, estamos listos para agregar las paginas [Link] al sitio web. Comencemos agregando [Link], nuestra pgina de inicio del sitio web. Haga clic en el nombre del proyecto en el explorador de soluciones y seleccione Agregar nuevo elemento. Elija la opcin WebForm de la lista de plantillas y llame al archivo [Link]. Adems seleccione la casilla Seleccionar pgina maestra.
Figura 5. Agregar un nuevo formulario web y marcar la casilla Seleccionar la pagina maestra. Despus de hacer clic en el botn Aceptar, se nos pide que elija la pgina maestra que esta nueva pgina [Link] debe utilizar. Aunque usted puede tener varias pginas maestras en el proyecto, aqu solo tenemos una. Despus de seleccionar la pgina maestra, las nuevas pginas [Link] contendrn el siguiente marcado:
[Link]
<%@ Page Language="VB" MasterPageFile="/[Link]" AutoEventWireup="true" CodeFile="[Link]" Inherits="_Default" Title="Untitled Page" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server"> </asp:Content>
Figura 6. Seleccione la pgina principal que esta pgina [Link] debe utilizar En la directiva @page hay una referencia al archivo usado de la pagina maestra (MasterPageFile=/[Link]) y en el marcado de las paginas [Link] un control Content para cada uno de los controles ContentPlaceHolder definidos en la pgina principal, con el ContentPlaceholderID asignamos el control Content a un ContentPlaceHolder especifico. El control Content es donde se coloca el marcado que deseamos que aparezca en el ContentPlaceHolder correspondiente. Establezca el atributo Title de la directiva @page en Home y agregue algn contenido de bienvenida al control Content:
[Link]
<%@ Page Language="VB" MasterPageFile="/[Link]" AutoEventWireup="true" CodeFile="[Link]" Inherits="_Default" Title="Home" %> <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server"> <h1>Welcome to the Working with Data Tutorial Site</h1> <p>This site is being built as part of a set of tutorials that illustrate some of the new data access and databinding features in [Link] 2.0 and Visual Web Developer.</p> <p>Over time, it will include a host of samples that demonstrate:</p>
<ul> <li>Building a DAL (data access layer),</li> <li>Using strongly typed TableAdapters and DataTables</li> <li>Master-Detail reports</li> <li>Filtering</li> <li>Paging,</li> <li>Two-way databinding,</li> <li>Editing,</li> <li>Deleting,</li> <li>Inserting,</li> <li>Hierarchical data browsing,</li> <li>Hierarchical drill-down,</li> <li>Optimistic concurrency,</li> <li>And more!</li> </ul> </asp:Content>
El atributo Title de la directiva @Page nos permite establecer el titulo de la pgina [Link], aunque el elemento Title es definido en la pgina maestra. Tambin puede establecer el titulo mediante programacin utilizando [Link]. Tambin tenga en cuenta que las referencias de la pgina maestra a las hojas de estilo ([Link]) son actualizadas automticamente para que funcionen en cualquier pagina [Link], sin importar si el directorio de la pgina [Link] est o no relacionado con la pagina principal. Cambiando a la vista de diseo, podremos ver como se ve la pgina en un navegador. Tenga en cuenta que en la vista diseo de la pgina [Link] solamente se pueden editar las regiones de contenido editable, las dems regiones no editables a diferencia del ContentPlaceHolder de la pgina maestra aparecen en gris.
Figura 7. La vista diseo de la pgina [Link] muestra tanto regiones editables como no editables Cuando la pgina [Link] es visitada desde un navegador, el motor [Link] combina automticamente el contenido de pgina de la pgina maestra y el contenido de la pgina [Link] y hace que el contenido se fusione en un HTML final que es enviado al navegador. Cuando el contenido de la pgina [Link] es actualizado, todas las pginas que usan esta pgina maestra combinaran nuevamente su contenido con el contenido de la pgina maestra, la prxima vez que sean solicitadas. En resumen el modelo de la pgina maestra, permite definir una plantilla de diseo nica (la pgina maestra) cuyos cambios se reflejan inmediatamente en todo el sitio. Agregar las paginas [Link] adicionales para nuestro sitio web Tommonos un momento para agregar ms paginas [Link] en el sitio que eventualmente almacenaran las demostraciones de varios informes. Habr ms de 35 demostraciones en total por lo que en lugar de crear todas las pginas, crearemos las primeras. Dado tambin que habr muchas categoras de demostraciones, para gestionar mejor las demostraciones agregamos una carpeta para cada una de las categoras. Por ahora aadimos las primeras tres carpetas: BasicReporting
Filtering CustomFormatting
Por ltimo agregamos nuevos archivos como se muestra en el explorador de soluciones de la figura 8. Al agregar cada uno de los archivos, no olvide seleccionar la casilla Seleccionar pgina maestra.
Figura 8. Agregar los siguientes archivos Paso 2: Creacin de un mapa del sitio Uno de los retos de gestionar un sitio web compuesto por un puado de pginas, es ofrecer un mecanismo sencillo para que los usuarios puedan navegar por el sitio. Para empezar se debe definir la estructura de navegacin del sitio. Luego esta estructura debe ser traducida en elementos de navegacin de interfaz de usuario como los mens o rutas de navegacin. Finalmente este proceso debe ser mantenido y actualizado para paginas nuevas agregadas al sitio y eliminaciones de las existentes. Antes de [Link] 2.0 los desarrolladores creaban su propia estructura de navegacin del sitio, la
mantenan y trasladaban esto en elementos de interfaz de navegacin para el usuario. Sin embargo con [Link] 2.0, los desarrolladores pueden usar un sistema de navegacin del sitio predeterminado y de uso flexible. El sistema de navegacin del sitio de [Link] 2.0 proporciona un medio para que el desarrollador defina un mapa del sitio y luego acceda a esta informacin a travs de la programacin de una API. [Link] 2.0 incluye un proveedor de mapas de sitio que espera los datos del mapa del sitio para almacenarlos en un archivo XML formateado de una manera particular. Como el sistema de navegacin del sitio est basado en el modelo del proveedor, este puede ser extendido para soportar formas alternativas para socializacin de informacin del mapa del sitio. El artculo de Jeff Prosise Que est esperando el proveedor del mapa del sitio muestra cmo crear un proveedor del mapa del sitio que almacena el mapa del sitio en una base de datos SQL Server, otra opcin es crear un proveedor de mapa del sitio basado en la estructura del sistema de archivos. Para este tutorial sin embargo usaremos el proveedor de mapa del sitio
predeterminado que se incluye con [Link] 2.0. Para crear el mapa del sitio, haga clic sobre el nombre del proyecto en el explorador de soluciones y escoja agregar nuevo elemento, luego seleccione la opcin Mapa del sitio. Deje el nombre en [Link] y haga clic en el botn Aceptar.
El archivo de mapa del sitio es un archivo XML. Tenga en cuenta que Visual Studio proporciona Intellisense para la estructura del mapa del sitio. El archivo de mapa del sitio debe tener el nodo <SiteMap> como nodo del nodo raz, el cual debe contener un elemento secundario <SiteMapNode>. El primer elemento del <SiteMapNode> puede contener un nmero arbitrario de elementos descendientes <SiteMapNode>. Defina el mapa del sitio para imitar la estructura del sistema de archivos. Es decir aada un elemento <SiteMapNode> para cada una de las tres carpetas y elementos <SiteMapNode> hijos para cada una de las pginas de [Link] de esas carpetas, as:
[Link]
<?xml version="1.0" encoding="utf-8" ?> <siteMap xmlns="[Link] > <siteMapNode url="/[Link]" title="Home" description="Home"> <siteMapNode title="Basic Reporting" url="/BasicReporting/[Link]" description="Basic Reporting Samples"> <siteMapNode url="/BasicReporting/[Link]" title="Simple Display" description="Displays the complete contents of a database table." /> <siteMapNode url="/BasicReporting/[Link]" title="Declarative Parameters" description="Displays a subset of the contents of a database table using parameters." /> <siteMapNode url="/BasicReporting/[Link]" title="Setting Parameter Values" description="Shows how to set parameter values programmatically." /> </siteMapNode> <siteMapNode title="Filtering Reports" url="/Filtering/[Link]" description="Samples of Reports that Support Filtering"> <siteMapNode url="/Filtering/[Link]" title="Filter by Drop-Down List" description="Filter results using a drop-down list." /> <siteMapNode url="/Filtering/[Link]" title="Master-Details-Details" description="Filter results two levels down." />
<siteMapNode url="/Filtering/[Link]" title="Details of Selected Row" description="Show detail results for a selected item in a GridView." /> </siteMapNode> <siteMapNode title="Customized Formatting" url="/CustomFormatting/[Link]" description="Samples of Reports Whose Formats are Customized"> <siteMapNode url="/CustomFormatting/[Link]" title="Format Colors" description="Format the grid s colors based on the underlying data." /> <siteMapNode url="/CustomFormatting/[Link]" title="Custom Content in a GridView" description="Shows using the TemplateField to customize the contents of a field in a GridView." /> <siteMapNode url="/CustomFormatting/[Link]" title="Custom Content in a DetailsView" description="Shows using the TemplateField to customize the contents of a field in a DetailsView." /> <siteMapNode url="/CustomFormatting/[Link]" title="Custom Content in a FormView" description="Illustrates using a FormView for a highly customized view." /> <siteMapNode url="/CustomFormatting/[Link]" title="Summary Data in Footer" description="Display summary data in the grids footer." /> </siteMapNode> </siteMapNode>
</siteMap>
El mapa del sitio define la estructura de navegacin del sitio web, la cual es una jerarqua del sitio. que define las diferentes secciones de un sitio. Cada elemento <SiteMapNode> en [Link] representa una seccin en la estructura de navegacin
Figura 10. El mapa del sitio representa una estructura de navegacin jerrquica [Link] expone la estructura del mapa del sitio por medio de la clase SiteMap del .Net Framework. Esta clase tiene una propiedad CurrentNode, que devuelve la informacin sobre la seccin que el usuario se encuentra visitando, la propiedad RootNode devuelve la raz del mapa del sitio (Home, en nuestro mapa del sitio). Tanto las propiedades CurrentNode y RootNode devuelven instancias SiteMapNode, las cuales tienen propiedades como ParentNode, ChildNodes, NextSibling, PreviousSibling y as sucesivamente, los cuales nos permiten navegar por la jerarqua del mapa del sitio. Paso 3: Mostrar un men basado en el mapa del sitio Acceder a los datos en [Link] 2.0 se puede lograr mediante programacin al igual que en [Link] 1.x, o mediante declaracin a travs de los nuevos controles de origen de datos. Existen varios controles de origen de datos preinstalados como el control SqlDataSource, para acceder a datos de bases relacionales, el control ObjectDataSource, para acceder a toda clase de datos y otros. Incluso usted puede crear sus propios objetos de origen de datos personalizados. Los controles de origen de datos sirven como un proxy entre tu pagina [Link] y los datos subyacentes. Con el fin de mostrar los valores devueltos por los controles de origen de datos, por lo general agregamos un control web y lo enlazamos a un control de origen de datos. Para enlazar un control web con un control de origen de datos, basta con establecer la propiedad DataSourceID del control web con el valor de la propiedad ID del control de origen de datos. Para ayudar con el trabajo de los datos del mapa del sitio, [Link] incluye el control SiteMapDataSource, el cual nos permite enlazar un control web con nuestro mapa del sitio. Dos controles web Treeview y Men se utilizan comnmente para proporcionar una interfaz de navegacin al usuario. Para enlazar los datos del mapa del sitio a uno
de estos controles, solo tenemos que aadir un SiteMapDataSource a la pgina, junto con un control Treeview o Men cuya propiedad DataSourceID se establece acordemente. Por ejemplo, podramos agregar un control men a la pgina a travs del siguiente marcado:
<div id="navigation"> <asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1"> </asp:Menu> <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" /> </div>
Para un grado de control sobre el HTML emitido, podemos enlazar al control SiteMapDataSource al control Repeater, as:
<div id="navigation"> <ul> <li><asp:HyperLink runat="server" ID="lnkHome" NavigateUrl="/[Link]">Home</asp:HyperLink></li> <asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1"> <ItemTemplate> <li> <asp:HyperLink runat="server" NavigateUrl='<%# Eval("Url") %>'> <%# Eval("Title") %></asp:HyperLink> </li> </ItemTemplate> </asp:Repeater> </ul> <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" ShowStartingNode="false" /> </div>
El control SiteMapDataSource devuelve la jerarqua del mapa del sitio, un nivel a la vez, empezando por el nodo raz del mapa del sitio (Home, en nuestro mapa del sitio),
luego el siguiente nivel (BasicReporting, Filtering Reports y Customized Formatting) y as sucesivamente. Cuando enlazamos el SiteMapDataSource al Repeater, este devuelve el primer nivel y crea instancias ItemTemplate para cada instancia SiteMapNode en ese nivel. . Para acceder a una propiedad particular del SiteMapNode, podemos usar Eval (propertyname), que es la forma en que recibimos las propiedades Url y Title de cada SiteMapNode para el control Hiperlink. En el ejemplo anterior el Repeater tendr el siguiente marcado:
<li> <a href="/Code/BasicReporting/[Link]">Basic Reporting</a> </li> <li> <a href="/Code/Filtering/[Link]">Filtering Reports</a> </li> <li> <a href="/Code/CustomFormatting/[Link]"> Customized Formatting</a> </li>
Estos nodos del mapa del sitio (BasicReporting, Filtering Reports y Customized Formatting) constituyen el segundo nivel del mapa del sitio que tenemos, no el primero. Esto se debe a que la propiedad ShowStartingNode del SiteMapDataSource, est establecida en Falso, haciendo que el SiteMapDataSource evite el nodo raz del mapa del sitio y en su lugar comienza devolviendo el segundo nivel en la jerarqua del mapa del sitio. Para mostrar los hijos de los SiteMapNode BasicReporting, Filtering Reports y Customized Formatting, podemos aadir otro Repeater para el ItemTemplate del Repeater inicial. Este segundo Repeater ser enlazado a la propiedad ChildNodes de la instancia SiteMapNode, as:
<asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1"> <ItemTemplate> <li> <asp:HyperLink runat="server" NavigateUrl='<%# Eval("Url") %>'>
<%# Eval("Title") %></asp:HyperLink> <asp:Repeater runat="server" DataSource="<%# CType([Link], SiteMapNode).ChildNodes %>"> <HeaderTemplate> <ul> </HeaderTemplate> <ItemTemplate> <li> <asp:HyperLink runat="server" NavigateUrl='<%# Eval("Url") %>'> <%# Eval("Title") %></asp:HyperLink> </li> </ItemTemplate> <FooterTemplate> </ul> </FooterTemplate> </asp:Repeater> </li> </ItemTemplate> </asp:Repeater>
Estos dos Repeater resultan en el siguiente marcado (algunas marcas se han quitado por cuestiones de brevedad)
<li> <a href="/Code/BasicReporting/[Link]">Basic Reporting</a> <ul> <li> <a href="/Code/BasicReporting/[Link]"> Simple Display</a> </li> <li> <a href="/Code/BasicReporting/[Link]"> Declarative Parameters</a> </li>
<li> <a href="/Code/BasicReporting/[Link]"> Setting Parameter Values</a> </li> </ul> </li> <li> <a href="/Code/Filtering/[Link]">Filtering Reports</a> ... </li> <li> <a href="/Code/CustomFormatting/[Link]"> Customized Formatting</a> ... </li>
Usamos estilos CSS escogidos del libro de Rachel Andrew La antologa CSS: 101 consejos esenciales, trucos y hacks, los elementos <ul> y <li> se denominan de tal forma que el marcado produce la salida visual mostrada en la Figura 11.
Figura 11. Un men compuesto por dos Repeaters y algunos estilos CSS Este men se encuentra en la pgina principal y es enlazado al mapa del sitio definido en [Link], lo que significa que cualquier cambio en el mapa del sitio se reflejar inmediatamente en todas las pginas que utilicen la pgina maestra [Link]. Desactivacin del ViewState Todos los controles [Link] pueden opcionalmente mantener su estado con el ViewState, el cual es serializado con un campo del formulario escondido en el cdigo HTML presentado. El ViewState es usado por los controles para recordar su estado cambiado mediante programacin a travs de las devoluciones de datos, como los datos enlazados a un control web. Si bien el ViewState permite que la informacin sea recordada a travs de las devoluciones de datos, este incrementa el tamao del marcado que debe ser enviado al cliente y puede ocasionar un serio crecimiento, si la pagina no es monitoreada de cerca. En los controles de datos web especialmente en los GridView es especialmente notorio que se aaden decenas de kilobytes extras de
marcado en una pgina. Si bien este aumento puede ser insignificante para los usuarios de banda ancha o de una intranet, el ViewState puede agregar varios segundos de ida y vuelta para los usuarios de dial manual. Para ver el impacto de ViewState visite una pgina en un navegador y luego observe el cdigo fuente enviado por la pagina web (en Internet Explorer, vaya a men ver y seleccione la opcin fuente). Usted tambin puede activar el seguimiento de pagina (page tracing) para ver la asignacin del ViewState usada por cada uno de los controles de la pgina. La informacin del ViewState es serializada en un campo oculto del formulario llamado _VIEWSTATE, situado en un elemento <div> inmediatamente despus de la apertura de las etiquetas <form>. El ViewState es solamente mantenido cuando hay un WebForm utilizndolo, si su pgina [Link] no incluye un <form runat=server> en su sintaxis declarativa, no habr un campo de formulario oculto _VIEWSTATE en el marcado que se representa. El campo del formulario _VIEWSTATE generado por la pgina maestra agrega aproximadamente 1800 bytes de marcado en la pgina. Este incremento extra se debe principalmente al control Repeater, ya que el contenido del control SiteMapDataSource se conserva en el ViewState. Mientras que un extra de 1800 bytes puede no parecer mucho para emocionarse, cuando se utiliza un GridView con muchos campos y registros, el ViewState puede aumentar en un factor de 10 o ms. El ViewState puede ser deshabilitado a nivel de pgina o de control, mediante el establecimiento de la propiedad EnableViewState en Falso, lo que reduce el tamao del marcado que se representa. Dado que el ViewState de un control de datos web mantiene los datos enlazados a los controles de datos web por medio de las devoluciones de datos, al desactivar el ViewState de un control de datos web, los datos deben estar enlazados en todas y cada uno de las devoluciones de datos. En la versin 1.x de [Link] esta responsabilidad recay sobre los hombros de los desarrolladores de pginas, con [Link] 2.0, sin embargo los datos de los controles web son nuevamente enlazados a su control de origen de datos, en cada devolucin de datos si es necesario. Para reducir el ViewState de la pgina, establecemos la propiedad EnableViewState de los controles Repeater en Falso. Esto puede realizarse a travs de la ventana
propiedades en el diseador o mediante declaracin en la vista cdigo fuente. Despus de hacer este cambio el marcado declarativo del Repeater ser similar a este:
<asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1" EnableViewState="False"> <ItemTemplate> ... <i>ItemTemplate contents omitted for brevity</i> ... </ItemTemplate> </asp:Repeater>
Despus de este cambio el tamao de la pagina presentada se redujo a solo 52 bytes, un 97% de ahorro en el tamao del ViewState. A lo largo de los tutoriales de esta serie vamos a desactivar de forma predeterminada el ViewState de los controles de datos web con el fin de reducir el tamao del marcado representado. En la mayora de los ejemplos la propiedad EnableViewState se establece en falso y no se menciona el hecho. La nica vez que el ViewState ser discutido es en los escenarios donde debe estar habilitado para que los controles de datos web puedan proporcionar la funcionalidad esperada. Paso 4: Agregar ruta de navegacin Para completar la pgina principal, vamos a agregar un elemento de ruta de navegacin como interfaz de usuario para cada pgina. La ruta de navegacin muestra rpidamente a los usuarios su posicin actual dentro de la jerarqua del sitio. Agregar una ruta de navegacin en [Link] 2.0 es fcil, solo agregamos un control SiteMapPath a la pagina, no necesita cdigo. Para nuestro sitio, agregar este control a la cabecera <div>:
<span class="breadcrumb"> <asp:SiteMapPath ID="SiteMapPath1" runat="server"> </asp:SiteMapPath> </span>
La ruta de navegacin muestra la pgina actual en la que se encuentra el usuario en la jerarqua del mapa del sitio, as como los nodos del mapa del sitio antecesores, hasta llegar a la raz (Home, en el mapa del sitio)
Figura 12. La ruta de navegacin muestra la pgina actual y sus antecesoras en el mapa del sitio Paso 5: Aadir la pgina por defecto para cada seccin Los tutoriales de nuestro sitio web, estn divididos en varias categoras, BasicReporting, Filtering Reports, Customized Formatting y as sucesivamente, con un carpeta para cada categora y los tutoriales correspondientes en las paginas [Link] dentro de esa carpeta. Cada carpeta tiene una pgina [Link]. En esta pgina mostraremos todos los tutoriales para la seccin actual. Es decir en la carpeta BasicReporting, la pgina [Link] tendr vnculos con [Link], [Link] y [Link]. Aqu una vez ms, podemos utilizar la clase SiteMap y un control de datos web para mostrar esta informacin basada en el mapa del sitio definido por el [Link]. Mostraremos nuevamente una lista desordenada con un Repeater, pero esta vez vamos a mostrar el titulo y la descripcin de los tutoriales. Como el marcado y el cdigo necesitan ser repetidos para cada pgina [Link], podemos encapsular esta interfaz lgica de usuario en un control de usuario. Creamos una carpeta en nuestro sitio web llamada UserControls y le aadimos un nuevo elemento de tipo control de usuario web llamado [Link] y agregamos el siguiente marcado:
[Link]
<%@ Control Language="VB" AutoEventWireup="true" CodeFile="[Link]" Inherits="UserControls_SectionLevelTutorialListing" %> <asp:Repeater ID="TutorialList" runat="server" EnableViewState="False"> <HeaderTemplate><ul></HeaderTemplate> <ItemTemplate> <li><asp:HyperLink runat="server" NavigateUrl='<%# Eval("Url") %>' Text='<%# Eval("Title") %>'></asp:HyperLink> - <%# Eval("Description") %></li> </ItemTemplate> <FooterTemplate></ul></FooterTemplate> </asp:Repeater>
[Link]
Partial Class UserControls_SectionLevelTutorialListing Inherits UserControl Protected Sub Page_Load(sender As Object, e As EventArgs) Handles [Link] If [Link] IsNot Nothing Then [Link] = [Link] [Link]() End If End Sub End Class
En el ejemplo anterior del Repeater, enlazamos los datos del SiteMap al Repeater mediante declaracin, sin embargo el control de usuario SectionLevelTutorialListing se realiz mediante programacin. En el controlador de eventos Page_Load se realiza una verificacin para asegurarse de que las Url de las pginas se asignan a un nodo del mapa del sitio. Si este control de usuario se utiliza en una pgina que no tiene su correspondiente entrada <SiteMapNode>, entonces [Link] devolver Nothing y ningn dato se enlazar al Repeater. Suponiendo que tenemos un CurrentNode, enlazamos su coleccin ChildNodes al Repeater. Como el mapa del sitio
est configurado de tal manera que la pagina [Link] de cada seccin es el nodo primario de todos los tutoriales de esa seccin, el cdigo mostrara vnculos y descripciones de todos los tutoriales de la seccin, como se muestra en la pantalla que aparece a continuacin. Una vez que se haya creado el Repeater, abra las paginas [Link] en cada una de las carpetas, vaya a vista diseo y basta con solo arrastrar el control de usuario desde el explorador de soluciones a la superficie de diseo en la que deseamos aparezca la lista de los tutoriales.
Figura 15. Los Tutoriales de reportes Bsicos son mostrados RESUMEN Con el mapa del sitio definido y la pgina principal completa, tenemos un diseo de pgina coherente y un esquema de navegacin para nuestros tutoriales de datos relacionados. Independientemente del nmero de pginas que aadimos a nuestro sitio, la actualizacin del diseo de pgina o la informacin de navegacin del sitio es un proceso de trabajo simple y rpido debido a que la informacin est centralizada. En concreto la informacin del diseo de pgina se define en la pgina maestra [Link] y el mapa del sitio en [Link]. No tuvimos necesidad de escribir ningn cdigo para lograr este gran diseo de pgina del sitio y el esquema de navegacin y nos reservamos la compatibilidad completa con el diseador de Visual Studio. Una vez completada la capa de acceso a datos y la capa lgica de negocios, teniendo un diseo de pgina coherente y la navegacin del sitio definida, estamos listos para comenzar a explorar los enfoques comunes de informacin. En los prximos tres tutoriales veremos tareas de reporte bsico mostrando datos devueltos desde la BLL en los controles GridView, FormView y DetailsView.
INFORMES BSICOS
Figura 1. [Link] 2.0 incluye cinco controles de origen de datos preinstalados El ObjectDataSource funciona como un proxy para trabajar con algn otro objeto. Para configurar el ObjectDataSource especificamos el objeto subyacente y como sus mtodos se asignaran a los mtodos Select, Update, Insert y Delete del el ObjectDataSource. Una vez que hemos especificado el objeto subyacente y sus mtodos se han asignado a los del ObjectDataSource, podemos enlazar ObjectDataSource a un control de datos Web. [Link] viene con muchos controles de datos web, incluyendo los controles GridView, DetailsView, RadioButtonList y DropDownList entre otros. Durante el ciclo de vida de la pgina, los controles de datos web podran necesitar acceder a sus datos enlazados, lo cual se realiza invocando el mtodo Select de su ObjectDataSource, si el control de datos web soporta llamadas de insercin actualizacin y eliminacin, las llamadas pueden hacerse a los mtodos Insert, Delete y Update de su ObjectDataSource. Estas llamadas luego se enrutan por el ObjectDataSource a los correspondientes mtodos del objeto subyacente, como se ilustra en el siguiente diagrama.
Figura 2. El ObjectDataSource sirve como proxy Aunque el ObjectDataSource se puede utilizar para invocar los mtodos para insertar, actualizar y eliminar datos, nos enfocaremos en la devolucin de datos, en tutoriales futuros exploraremos el uso del ObjectDataSource y los controles de datos web para modificar datos. Paso 1: Agregar y configurar el control ObjectDataSource Comenzamos abriendo la pgina [Link] de la carpeta BasicReporting, cambie a la vista diseo y luego arrastre un control ObjectDataSource desde la caja de herramientas hasta la superficie de diseo de la pgina. El ObjectDataSource aparece como un cuadro gris en la superficie de diseo, ya que no produce ningn marcado; este simplemente accede a los datos invocando un mtodo desde un objeto especifico. Los datos devueltos por un ObjectDataSource pueden ser mostrados por un control de datos web, como los controles GridView, FormView, DetailsView y as sucesivamente. Nota: De forma alternativa, podemos agregar los controles de datos web a la pgina y luego desde la etiqueta inteligente, elija la opcin <Nuevo origen de datos> de la lista desplegable. Para especificar el objeto subyacente del ObjectDataSource y como los mtodos de ese objeto se asignan a los del ObjectDataSource, haga clic en el enlace configurar origen de datos desde la etiqueta del ObjectDataSource.
Figura 3. Haga clic en el enlace Configurar Origen de datos desde la etiqueta inteligente El resultado ser el asistente de configuracin de origen de datos. Primero debemos especificar el objeto con el cual trabaja el ObjectDataSource. Si la opcin Mo strar solo componentes de datos esta activada, la lista desplegable mostrada en esta pantalla, solo mostrar aquellos objetos que han sido decorados con el atributo DataObject. Actualmente la lista incluye los TableAdapters en el DataSet tipiado y las clases BLL que hemos creado en el tutorial anterior. Si usted olvido agregar el atributo DataObject a la capa lgica de negocios, entonces no podr verlas en la lista. En este caso, desactive la casilla Mostrar solo componentes de datos para ver todos los objetos, lo cual incluye las clases BLL (junto con las otras clases en el DataSet tipiado, los DataTables, DataRows y as sucesivamente). En la primera pantalla, seleccione la clase ProductsBLL del la lista desplegable y haga clic en siguiente.
La siguiente pantalla del asistente le pide que seleccione el mtodo que el ObjectDataSource debe invocar. El cuadro combinado muestra la lista de los mtodos que devuelven datos en el objeto seleccionado en la pantalla anterior. Aqu vemos GetProductByProductId, GetProducts, GetProductsByCategoryID y GetProductsBySupplierID. Seleccione el mtodo GetProducts de la lista desplegable y haga clic en Finalizar (Si ha agregado DataObjectMethodAttribute a los mtodos de la ProductsBLL, como se muestra en el tutorial anterior, esta opcin estar seleccionada por defecto).
Figura 5. Elija mtodos para devolver datos desde la pestaa SELECT Configure manualmente el ObjectDataSource El asistente de configuracin de origen de datos del ObjectDataSource ofrece una forma rpida de especificar el objeto que utiliza y como asociar los mtodos del objeto que son invocados. Sin embargo se puede configurar el ObjectDataSource por medio de sus propiedades, ya sea a travs de la ventana Propiedades o a travs del marcado declarativo. Sencillamente establezca la propiedad TypeName del tipo de objeto subyacente que va a utilizar y el SelectMethod al mtodo para invocar cuando recuperamos datos.
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetProducts" TypeName="ProductsBLL"> </asp:ObjectDataSource>
Incluso si prefiere el asistente de configuracin de origen de datos puede haber ocasiones en las que es necesario configurar manualmente el ObjectDataSource, ya que el asistente solo muestra las clases creadas por el desarrollador. Si desea enlazar el ObjectDataSource a una clase en el .Net Framework como la clase MemberShip, para acceder a la informacin de la cuenta de usuario o la clase Directory para trabajar con la informacin de los archivos del sistema, necesitara configurar manualmente las propiedades del ObjectDataSource. Paso 2: Agregar un control de datos web y enlazarlo al ObjectDataSource Una vez que ha agregado el ObjectDataSource a la pgina y lo ha configurado, estamos listos para agregar controles de datos web a la pgina para mostrar los datos devueltos por el mtodo Select del ObjectDataSource. Cualquier control de datos web puede enlazarse a un ObjectDataSource, veremos como mostrar los datos del ObjectDataSource en el GridView, FormView y DetailsView. Enlazar un GridView al ObjectDataSource Agregue un control GridView desde el cuadro de herramientas a la superficie de diseo de la pgina [Link]. Desde la etiqueta inteligente del GridView, elija el control ObjectDataSource que se agrego en el paso 1. Esto creara automticamente un BoundField en el control GridView para cada propiedad que devuelva los datos del mtodo Select del ObjectDataSource (es decir las propiedades definidas por el DataTable Products).
Luego puede personalizar, reorganizar o eliminar los BoundFields en el GridView, haga clic en la opcin Editar Columnas en la etiqueta inteligente.
Figura 7. Administrar los BoundFields del GridView por medio del cuadro de dialogo Editar Columnas Tmese un tiempo para modificar los BoundFields del GridView, eliminando los BoundFields ProductID, SupplierID, CategoryID, QuantityPerUnit, UnitsInStock, UnitsOnOrder y ReorderLevel. Basta con seleccionar el BoundField de la lista inferior del lado izquierdo y hacer clic en el botn eliminar (en la X roja) para eliminarlos. Luego reorganice los BoundFields para que los BoundFields CategoryName y SupplierName precedan al BoundField UnitPrice seleccionando estos BoundFields y haciendo clic en la flecha hacia arriba. Ajuste las propiedades HeaderText de los BoundFields restantes a Products, Category, Supplier y Price respectivamente. Luego de formato al BoundField Price como moneda ajustando la propiedad HtmlEncode del BoundField en Falso y su propiedad DataFormatString en {0:c}. Para finalizar alinee horizontalmente el precio a la derecha y centre la casilla de verificacin Discontinued por medio de la propiedad ItemStyle/HorizontalAlign.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" EnableViewState="False"> <Columns> <asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" /> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True" SortExpression="SupplierName" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice"> <ItemStyle HorizontalAlign="Right" /> </asp:BoundField> <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued"> <ItemStyle HorizontalAlign="Center" /> </asp:CheckBoxField> </Columns> </asp:GridView>
Figura 8. Los BoundFields del GridView han sido personalizados Uso de temas para un look consistente Estos tutoriales se esfuerzan por remover cualquier configuracin de estilos a nivel de controles, en su lugar siempre que sea posible usamos hojas de estilo definidas en un archivo externo. El archivo [Link] contiene clases CSS DataWebControlStyle, HeaderStyle, RowStyle y AlternatingRowStyle que deben usarse para dictar la apariencia de los controles de datos web usados en estos tutoriales. Para realizar esto, podramos establecer la propiedad CssClass del GridView a DataWebControlStyle y sus
propiedades HeaderStyle, RowStyle y AlternatingRowStyle a las propiedades Css correspondientes. Si establecemos estas propiedades CssClass en el control Web, necesitaremos recordar establecer explcitamente los valores de estas propiedades para todos y cada uno de los controles de datos web de nuestros tutoriales. Un enfoque ms manejable es definir por defecto las propiedades Css relacionadas para los controles GridView, FormView y DetailsView utilizando un tema. Un tema es una coleccin de propiedades, imgenes y clases Css a nivel de control que se pueden aplicar a las pginas de un sitio para hacer cumplir un look comn. Nuestro tema no incluye imgenes o archivos Css (dejaremos la hoja de estilos [Link] como est definida en la carpeta raz de la aplicacin web) pero contar con dos skins. El skin es un archivo que define las propiedades por defecto de un control web. En concreto vamos a tener un archivo skin para los controles GridView y DetailsView, que indica por defecto las propiedades Css relacionadas. Comience agregando un archivo skin a su proyecto denominado [Link] haciendo clic derecho sobre el nombre del proyecto en el Explorador de Soluciones y elija agregar nuevo elemento.
Figura 9. Aada un archivo piel llamado [Link] Los archivos skin deben ser colocados en un tema, los cuales estn localizados en la carpeta App_Themes. Puesto que aun no tiene ninguna carpeta, Visual Studio amablemente se ofrece a crearla por nosotros cuando creamos nuestro primer skin.
Haga clic en S para crear la carpeta App_Themes y colocar all el nuevo archivo [Link].
Figura 10. Visual Studio crear la carpeta App_Themes Esto creara un nuevo tema en la carpeta App_Themes llamada GridView con el archivo [Link].
Figura 11. El tema GridView ha sido agregado a la carpeta App_Themes Cambie el tema GridView a DataWebControls (haga clic derecho sobre la subcarpeta GridView en la carpeta GridView y seleccione cambiar nombre). Luego introduzca el siguiente marcado en el archivo [Link]:
<asp:GridView runat="server" CssClass="DataWebControlStyle"> <AlternatingRowStyle CssClass="AlternatingRowStyle" /> <RowStyle CssClass="RowStyle" /> <HeaderStyle CssClass="HeaderStyle" /> </asp:GridView>
Esto define por defecto las propiedades para las propiedades CssClass relacionadas para cualquier GridView en cualquier pgina que utilice el tema DataWebControls.
Aadiremos otro skin para el DetailsView, un control de datos web que utilizaremos en breve. Aada un nuevo skin para el tema DataWebControls llamado [Link] y agregue el siguiente marcado:
<asp:DetailsView runat="server" CssClass="DataWebControlStyle"> <AlternatingRowStyle CssClass="AlternatingRowStyle" /> <RowStyle CssClass="RowStyle" /> <FieldHeaderStyle CssClass="HeaderStyle" /> </asp:DetailsView>
Con nuestro tema definido, el ltimo paso es aplicar el tema a nuestra pgina de [Link]. Un tema puede ser aplicado en forma de pgina a pgina o para todas las pginas del sitio web. Usaremos este tema para todas las pginas del sitio web. Para realizar esto, agregamos el siguiente marcado a la seccin <[Link]> del [Link].
<pages styleSheetTheme="DataWebControls" />
Esto es todo lo que hay que hacer! El valor del StyleSheetTheme indica que las propiedades especificadas en el tema, no deben ser remplazadas por las propiedades definidas a nivel de control. Para especificar que la configuracin del tema debe estar por encima de los ajustes de control, se utiliza el atributo tema en lugar de StyleSheetTheme, por desgracia la configuracin del tema no aparece en la vista de diseo de Visual Studio. Consulte los temas Un vistazo a los temas y mascaras en [Link] y Estilos del lado-servidor utilizando temas para obtener ms informacin sobre temas y mscaras; Ver cmo aplicar temas de [Link] para mayor informacin sobre la configuracin de una pgina para utilizar un tema.
Figura 12. El GridView muestra la informacin ProductName, Category, Supplier, Price y Discontinued Mostrando un registro a la vez en un DetailsView El GridView muestra una fila para cada registro devuelto por el control de origen de datos al que esta enlazado. Sin embargo, hay ocasiones en que deseamos mostrar nicamente un registro o solo un registro a la vez. El control DetailsView ofrece esta funcionalidad, hacindolo como un table <HTML> con dos columnas y una fila por cada columna o propiedad enlazada al control. Usted puede pensar en el DetailsView como un GridView con un registro nico rotado 90 grados. Comenzamos agregando un control DetailsView encima del GridView en la pgina [Link] Luego enlazamos el control al mismo ObjectDataSource como con el GridView. Al igual que con el GridView se aadir un BoundField al DetailsView por cada propiedad de vuelta en el objeto por el mtodo Select del ObjectDataSource. La nica diferencia es que en el DetailsView los BoundFields estn dispuestos horizontalmente y no verticalmente.
Figura 13. Aadir un DetailsView a la pgina y enlazarlo al ObjectDataSource Al igual que en el GridView, los BoundFields del DetailsView pueden ser ajustados para ofrecer una visualizacin ms personalizada de los datos devueltos por el ObjectDataSource. La figura 14 muestra el DetailsView luego de que sus BoundFields y sus propiedades CssClass han sido configuradas para dar un aspecto similar al del ejemplo con el GridView.
Figura 14. El DetailsView muestra un solo registro Tenga en cuenta que el DetailsView solo muestra el primer registro devuelto por su origen de datos. Para permitir al usuario pasar uno a uno a travs de todos los registros, debemos habilitar la paginacin en el DetailsView. Para ello regresamos a Visual Studio y seleccionamos la casilla Habilitar Paginacin en la etiqueta inteligente del DetailsView.
Figura 16. Con la paginacin habilitada, el DetailsView permite al usuario ver cualquiera de los productos Hablaremos ms acerca de la paginacin en futuros tutoriales.
Un diseo ms flexible para mostrar un solo registro a la vez El DetailsView es bastante rgido en la forma en que muestra cada registro devuelto por el ObjectDataSource. Podramos desear una visin ms flexible de los datos. Por ejemplo, en lugar de querer mostrar el nombre del Producto, Categora, Proveedor, Precios e informacin de Descontinuacin cada uno en una fila, es posible que deseemos mostrar el nombre del producto y el precio en un encabezamiento <h4>, y la informacin de la categora y el proveedor debajo de ellos con un tamao de fuente ms pequeo. Tambin podramos no querer mostrar los nombres de las propiedades (Productos, categoras y otros) al lado de los valores. El control FormView proporciona este nivel de personalizacin. En lugar de utilizar campos (como lo hacen el GridView y el DetailsView), el FormView utiliza plantillas, que permiten una mezcla de controles., HTML esttico y sintaxis de enlace de datos. Si est familiarizado con el control Repeater desde [Link] 1.x, puede pensar en el FormView como un Repeater para mostrar un solo registro. Agregue un FormView a la superficie de diseo de la pgina [Link]. Inicialmente el FormView se muestra como un bloque gris, informndonos que tenemos que ofrecer como mnimo el control ItemTemplate.
Usted puede enlazar directamente el FormView a un control de origen de datos por medio de la etiqueta inteligente del FormView, el cual crear automticamente por defecto un ItemTemplate. (junto con un EditItemTemplate y un InsertItemTemplate si las propiedades InsertMethod e UpdateMethod del control ObjectDataSource han sido establecidas). Sin embargo para este ejemplo enlazaremos los datos al FormView y estableceremos su ItemTemplate manualmente. Comenzaremos estableciendo la propiedad DataSourceID del FormView al ID del control ObjectDataSource, ObjectDataSource1. Luego creamos el ItemTemplate para que muestre el nombre del producto y su precio en un elemento <h4> y la categora y el nombre del proveedor debajo en un tamao de fuente ms pequeo.
<asp:FormView ID="FormView1" runat="server" DataSourceID="ObjectDataSource1" EnableViewState="False"> <ItemTemplate> <h4> <%# Eval("ProductName") %> (<%# Eval("UnitPrice", "{0:c}") %>) </h4> Category: <%# Eval("CategoryName") %>; Supplier: <%# Eval("SupplierName") %> </ItemTemplate> </asp:FormView>
El <%Eval(PropertyName)%> es la sintaxis de enlace a datos. El mtodo Eval devuelve el valor de la propiedad especificada para el objeto que est siendo actualmente enlazada al control FormView. Echemos un vistazo al artculo Sintaxis de Enlace de datos simplificado y ampliado en [Link] 2.0 de Alex de Homero para tener ms informacin sobre las entradas y salidas de enlace de datos. Al igual que el DetailsView, el FormView solo muestra el primer registro devuelto por el ObjectDataSource. Usted puede habilitar la paginacin en el FormView para permitir a los usuarios recorrer los productos uno a la vez. RESUMEN El acceso y la visualizacin de datos de una capa lgica de negocios se pueden lograr sin necesidad de escribir una lnea de cdigo gracias al control ObjectDataSource de [Link] 2.0. El control ObjectDataSource llama a un mtodo especfico de una clase y devuelve los resultados. Estos resultados se pueden mostrar en un control de datos web enlazado al control ObjectDataSource. En este tutorial observamos el enlace del ObjectDataSource a los controles GridView, FormView y DetailsView. Hasta ahora solo hemos visto como usar el ObjectDataSource para invocar un mtodo sin parmetros, pero y si deseamos invocar un mtodo que espera parmetros de entrada, tal como el GetproductsByCategoryID (categoryID) de la clase ProductsBLL? Con el fin llamar a un mtodo que espera uno o ms parmetros debemos configurar el ObjectDataSource para especificar los valores de estos parmetros. Veremos cmo lograr esto en nuestro prximo tutorial.
5. DECLARACIN DE PARAMETROS
En este tutorial vamos a ilustrar como usar un parmetro estableciendo un valor no modificable para seleccionar los datos a mostrar en un control DetailsView. Introduccin En el tutorial anterior vimos como mostrar los datos con los Controles GridView, DetailsView y FormView enlazados a un control ObjectDataSource que invocaba el mtodo GetProducts () de la clase ProductsBLL. El mtodo GetProducts () devuelve un DataTable fuertemente tipiado poblado con todos los registros de la base de datos de la tabla Products. La clase ProductsBLL contiene mtodos adicionales para la devolucin de subconjuntos de los productos GetProductsByProductID (productID), GetProductsByCategoryID (categoryID) y GetProductsBySupplierID (supplierID). Estos tres mtodos esperan un parmetro de entrada que indica como filtrar la informacin devuelta de los productos. El ObjectDataSource se puede utilizar para llamar mtodos que esperan parmetros de entrada, pero para hacerlo tenemos que especificar el lugar de donde vienen los valores de los parmetros. Los valores de los parmetros pueden ser fuertemente codificados o provenir de fuentes dinmicas, incluyendo: valores de cadenas de consulta, variables de sesin, el valor de una propiedad de un control web de la pgina, entre otros. Para este tutorial vamos a empezar mostrando cmo usar un parmetro establecido como un valor no modificable. En concreto veremos la adicin de un DetailsView que muestra la informacin de un producto especfico de nombre Mix Gumbo del Chef Anton, el cual tiene un ProductID de 5. Luego veremos cmo establecer el valor de los parmetros basados en un control web. En particular, utilizaremos un cuadro de texto para permitir que el usuario ingrese un pas, despus de lo cual puede hacer clic en un botn para ver la lista de proveedores que residen en ese pas. Usando un valor de parmetro no modificable Para el primer ejemplo empezaremos agregando un control DetailsView a la pgina [Link] de la carpeta BasicReporting. Desde la etiqueta inteligente del DetailsView, seleccione <Nuevo Origen de datos> en la lista desplegable y elija agregar un ObjectDataSource.
Figura 1. Aadir un ObjectDataSource a la pgina Esto iniciara automticamente el asistente de configuracin de Origen de datos del control ObjectDataSource. Seleccione la clase ProductsBLL en la primera pantalla del asistente.
Figura 2. Seleccione la clase ProductsBLL Como queremos mostrar la informacin sobre un producto en particular, necesitamos usar el mtodo GetProductsByProductID (productID).
Figura 3. Elija el mtodo GetProductsByProductID (productID) Dado que el mtodo que se ha seleccionado incluye un parmetro, hay una pantalla adicional en el asistente, donde se nos pide definir el valor que se utilizara para el parmetro. La lista de la izquierda muestra todos los parmetros del mtodo seleccionado. Para el mtodo GetProductByProductID (productID) solo hay uno, el productID. A la derecha podemos especificar el valor del parmetro seleccionado. La lista desplegable muestra las posibles fuentes de los parmetros. Puesto que se desea especificar un valor no modificable de 5 para el parmetro ProductID, dejamos la fuente del parmetro en Ninguno y escribimos 5 en el cuadro de texto de valor por defecto.
Figura 4. Usaremos un valor de parmetro de 5 fuertemente codificado para el parmetro productID Despus de completar el asistente de configuracin del origen de datos, el marcado declarativo del control ObjectDataSource incluye un objeto Parameter en la coleccin SelectParameters para cada uno de los parmetros de entrada esperados por el mtodo definido en la propiedad SelectMethod. Dado que el mtodo que estamos especificando solo espera un parmetro de entrada, ParameterID es la nica entrada aqu. La coleccin SelectParameters puede contener cualquier clase que se derive de la clase Parameter en el espacio de nombres [Link]. Para los valores de parmetros no modificables se utiliza la clase base Parameter, pero para las otras opciones de origen de parmetros se utiliza una clase Parameter derivada; si es necesario tambin podemos crear nuestros propios tipos parameter personalizados.
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetProductByProductID" TypeName="ProductsBLL"> <SelectParameters> <asp:Parameter DefaultValue="5" Name="productID" Type="Int32" /> </SelectParameters> </asp:ObjectDataSource>
Nota: Si has ido siguiendo los pasos en tu computador, el marcado declarativo que debes ver en este momento incluye valores para las propiedades InsertMethod,
UpdateMethod
DeleteMethod,
as
como
DeleteParameters.
El
asistente
de
configuracin de origen de datos del ObjectDataSource define automticamente los mtodos del ProductsBLL para actualizar, insertar y eliminar, por lo tanto a menos que los especifique claramente por fuera, sern incluidos en el anterior marcado. Cuando visitamos la pgina, el control de datos web invoca el mtodo Select del ObjectDataSource, el cual llamar el mtodo GetproductsByProductID (productID) de la clase ProductsBLL, empleando el valor no modificable de 5 para el parmetro de entrada productID. El mtodo devuelve un objeto ProductsDataTable fuertemente tipiado que incluye una sola fila con informacin acerca de Gumbo Mix del chef Anton (el producto con el productID de 5)
Figura 5. Se muestra la informacin acerca del Gumbo Mix del Chef Anton Establecer el valor del parmetro a la propiedad valor de un control Web Los valores de los parmetros del ObjectDataSource tambin pueden establecerse en funcin del valor de un control web de la pgina. Para ilustrar esto vamos a tener un GridView que muestre todos los proveedores que se encuentren en un pas determinado por el usuario. Para realizar esto, comenzamos agregando un control TextBox a la pgina, en el cual el usuario puede introducir un nombre del pas. Ajuste la propiedad ID del control TextBox en CountryName. Agregue tambin un control web Button.
Figura 6. Agregar un control TextBox a la pgina con su ID en CountryName Luego agregue un control GridView a la pgina y desde la etiqueta inteligente aada un nuevo ObjectDataSource. Como queremos mostrar la informacin del proveedor, seleccionamos la clase SuppliersBLL en la primera pantalla del asistente. En la segunda pantalla elegimos el mtodo GetSuppliersByCountry (country).
Figura 7. Elija el mtodo GetSuppliersByCountry (country) Como el mtodo GetSuppliersByCountry (country) tiene un parmetro de entrada, el asistente incluye una nueva pantalla para elegir el valor del parmetro. Esa vez establecemos el origen del parmetro en Control. Esto poblar la lista desplegable ControlID con los nombres de los controles de la pgina, seleccione el control CountryName de la lista. Cuando se visita por primera vez la pgina, el TextBox CountryName estar en blanco, por lo que no se devuelven resultados y no aparecer
nada. Si deseamos mostrar algunos resultados de forma predeterminada, entonces establezca el DefaultValue del TextBox.
Figura 8. Establezca el valor del parmetro al valor del control CountryName El marcado declarativo del ObjectDataSource difiere ligeramente del de nuestro primer ejemplo, ya que utiliza un ControlParameter en lugar de un objeto Parameter estndar. Un ControlParameter tiene propiedades adicionales para especificar el ID del control Web y el valor de la propiedad que se usara para el parmetro (PropertyName). El asistente de configuracin de origen de datos es lo suficientemente inteligente para determinar que para un TextBox es posible que se utilice la propiedad Text para el valor del parmetro. Sin embargo si desea usar un valor de propiedad diferente del control web, puede cambiar aqu el valor de PropertyName o pulsando el botn Mostrar propiedades avanzadas en el asistente.
<asp:ObjectDataSource ID="ObjectDataSource2" runat="server" SelectMethod="GetSuppliersByCountry" TypeName="SuppliersBLL"> <SelectParameters> <asp:ControlParameter ControlID="CountryName" Name="country" PropertyName="Text" Type="String" /> </SelectParameters> </asp:ObjectDataSource>
Cuando visitamos la pgina por primera vez el TextBox CountryName esta vacio. El mtodo Select del ObjectDataSource es invocado por el GridView, pero un valor de Nothing es pasado al mtodo GetSuppliersByCountry (country). El TableAdapter convierte el Nothing a un valor Null para la base de datos ([Link]), pero la consulta utilizada por el mtodo GetSuppliersByCountry (country) est escrita de tal forma que no devuelve ningn valor cuando se especifica un valor de Null para el parmetro @CategoryID. En resumen no se devuelve ningn proveedor. Una vez que el usuario ingresa un pas y hace clic en el botn Mostrar proveedores se origina una devolucin de datos, el mtodo Select del ObjectDataSource es requerido, pasando el valor Text del control TextBox como el parmetro Country.
Figura 9. Se muestran los proveedores de Canada Mostrar todos los proveedores de forma predeterminada En lugar de no mostrar ningn proveedor la primera vez que ingresamos a la pgina, deseamos mostrar todos los proveedores en un primer momento, lo que permitir al usuario filtrar la lista al introducir un nombre de pas en el cuadro de texto. Cuando el cuadro de texto est vaco, el mtodo GetSuppliersByCountry (country) de la clase SuppliersBLL pasa un valor de Nothing como su parmetro de entrada Country. Este valor de Nothing es transmitido al mtodo GetSuppliersByCountry de la DAL donde es traducido a un valor Null para la base de datos para el parmetro Country en la siguiente consulta:
SELECT FROM SupplierID, CompanyName, Address, City, Country, Phone Suppliers
La expresin Country=Null siempre devolver falso, incluso para aquellos registros cuya columna Country tienen un valor Null, por lo tanto no se devuelven registros.
Para devolver todos los proveedores, cuando el TextBox Country esta vacio, podemos ampliar el mtodo GetSuppliersByCountry (country) en la BLL para invocar el mtodo GetSuppliers () cuando su parmetro pas es Nothing o en caso contrario llama al mtodo GetSuppliersByCountry (country). Esto tendr el efecto de devolver todos los proveedores cuando no se especifique ningn pas y devolver el subconjunto de proveedores cuando se incluye un valor para el parmetro pas. Cambie el mtodo GetSuppliersByCountry (country) del mtodo SuppliersBLL a lo siguiente:
Public Function GetSuppliersByCountry(country As String) _ As [Link] If [Link](country) Then Return GetSuppliers() Else Return [Link](country) End If End Function
Con este cambio la pgina [Link] muestra todos los proveedores, cuando la visitamos por primera vez (o cada vez que el TextBox CountryName este vaco).
Figura 10. Todos los proveedores se muestran de forma predeterminada Resumen Con el fin de especificar mtodos con parmetros de entrada, es necesario especificar los valores de los parmetros de la coleccin SelectParameters del ObjectDataSource. Los diferentes tipos de parmetros permiten que el valor del parmetro sea obtenido de diferentes fuentes. El tipo de parmetro por defecto utiliza un valor no modificable, pero con la misma facilidad (y sin una lnea de cdigo) los valores de los parmetros pueden ser obtenidos de una cadena de consulta, variables de sesin, cookies, e incluso valores ingresados por el usuario en los controles web de la pgina. Los ejemplos que vimos en este tutorial muestran cmo utilizar valores de parmetros mediante declaracin. Sin embargo pude haber momentos en que deseamos utilizar una fuente parmetro que no est disponible, como por ejemplo la fecha y hora actuales, o si nuestro sitio utiliza Membership (suscripcin), el ID del usuario visitante. Para estos escenarios se pueden establecer los valores de los parmetros mediante programacin antes de invocar el mtodo de su objeto subyacente. Veremos cmo realizar en el siguiente tutorial.
Figura 1. Los eventos Selecting y Select del ObjectDataSource se generan antes y despus de invocar el mtodo del objeto subyacente En este tutorial veremos la adicin de un mtodo a nuestra DAL y BLL que acepta un solo parmetro de entrada Month, de tipo Integer y devuelve un objeto EmployeesDataTable poblado con aquellos empleados cuyo aniversario de contratacin es en el mes especificado Nuestro ejemplo establece este parmetro mediante programacin basado en el mes en curso y mostrando una lista de Empleados de aniversario este mes Paso 1: Agregar un mtodo al EmployeesTableAdapter Para nuestro primer ejemplo tenemos que aadir un mtodo para recuperar aquellos empleados cuya HireDate ocurri en un determinado mes. Para proporcionar esta funcionalidad, de acuerdo con nuestra arquitectura tenemos que crear un mtodo en el EmployeesTableAdapter que se asigne a la sentencia SQL adecuada. Para realizar esto primero abrimos el DataSet tipiado Northwind. Haga clic en la etiqueta EmployeesTableAdapter y elija agregar consulta.
Figura 2. Agregue una nueva consulta a EmployeesTableAdapter Seleccione agregar una instruccin Select que devuelva filas. Cuando lleguemos a la pantalla de declaracin especfica de la sentencia Select, la sentencia SELECT por defecto para el EmployeesTableAdapter ya est cargada. Solo tenemos que agregar la clausula WHERE DATEPART(m, HireDate)=@Month. DATEPART es una funcin T-SQL que devuelve una parte en particular de una fecha determinada de tipo datetime, en este caso estamos utilizando DATEPART para devolver el mes de la columna HireDate.
Figura 3. Devuelve solo las filas donde la columna HireDate es menor o igual al parmetro @HiredBeforeDate
Para
finalizar
cambie
los
nombres
de
los
mtodos
FillBy
GetData
Figura 4. Elija los nombres ms adecuados para los mtodos FillBy y GetDataBy Haga clic en Finalizar para completar el asistente y regresar a la superficie de diseo del DataSet. El EmployeesTableAdapter ahora debe incluir un nuevo conjunto de mtodos para acceder a los empleados contratados en un determinado mes.
Figura 5. Los nuevos mtodos aparecen en la superficie de diseo del DataSet Paso 2: Agregar al mtodo GetEmployeesByHiredDateMonth (month) a la capa lgica de negocio
Como nuestra arquitectura de aplicacin utiliza una capa separada de lgica de negocios y lgica de acceso a datos, tenemos que aadir un mtodo a nuestra BLL que llame a la DAL para recuperar los empleados que fueron contratados antes de una fecha determinada. Abra el archivo [Link] y agregue el siguiente mtodo:
<[Link] _ ([Link], False)> _ Public Function GetEmployeesByHiredDateMonth(ByVal month As Integer) _ As [Link] Return [Link](month) End Function
Al igual que los otros mtodos de esta clase, GetEmployeesByHiredDateMonth (month) simplemente llama a la DAL y devuelve los resultados. Paso 3: Mostrar los empleados cuyo aniversario de contratacin es este mes El paso final para este ejemplo es mostrar aquellos empleados cuyo aniversario de contratacin es este mes. en Comience la carpeta con agregando un GridView y a la un pgina nuevo en [Link] utilice la clase BasicReporting el agregue
ObjectDataSource como origen de datos. Configure el ObjectDataSource para que EmployeesBLL SelectMethod establecido GetEmployeesByHiredDateMonth (month).
Figura 7. Seleccione el mtodo GetEmployeesByHiredDateMonth (month) La ltima pantalla nos pide suministrar el origen del valor del parmetro month. Puesto que vamos a establecer este valor mediante programacin, dejamos la opcin de origen del parmetro en Ninguno y hacemos clic en Finalizar.
Figura 8. Deje establecido el origen del parmetro en Ninguno Esto crear un objeto Parameter en la coleccin SelectParameters del ObjectDataSource que no tiene un valor especificado.
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" OldValuesParameterFormatString="original_{0}" SelectMethod="GetEmployeesByHiredDateMonth" TypeName="EmployeesBLL">
Para establecer este valor mediante programacin, necesitamos crear un controlador de eventos para el evento Selecting del ObjectDataSource. Para realizar esto vaya a la Vista Diseo y haga doble clic en el ObjectDataSource. Tambin puede seleccionar el ObjectDataSource, ir a la ventana propiedades y hacer doble clic en el icono del rayo. Luego haga doble clic en el cuadro de texto al lado del evento Selecting o escriba el nombre del controlador de eventos que desee utilizar. Como tercera opcin, puede crear el controlador de eventos mediante la seleccin del ObjectDataSource y su evento Selecting en las dos listas desplegables en la parte superior de la vista cdigo de la pgina de la clase subyacente.
Figura 9. Haga clic en el icono del rayo en la ventana propiedades para listar los eventos del control web Los tres enfoques agregan un nuevo controlador de eventos para el evento Selecting del ObjectDataSource a la clase de cdigo subyacente de la pgina. En este controlador de eventos podemos leer y escribir los valores de los parmetros utilizando [Link](parameterName), donde parameterName es el valor del atributo Name en la etiqueta <asp:Parameter> (la coleccin InputParameters tambin se puede
indexar ordinalmente, como en [Link] (index)). Para establecer el parmetro month al mes actual, agregamos el siguiente controlador para el evento Selecting:
Protected Sub ObjectDataSource1_Selecting _ (sender As Object, e As ObjectDataSourceSelectingEventArgs) _ Handles [Link] [Link]("month") = [Link] End Sub
Cuando visitamos esta pgina a travs del navegador, podemos observar que solamente un empleado fue contratado este mes (marzo) Laura Callahan, quien ha estado en la compaa desde 1994.
Figura 10. Se muestran los empleados cuyo aniversario es este mes Resumen Si bien los valores de los parmetros del ObjectDataSource normalmente se pueden establecer mediante declaracin, sin necesidad de escribir una lnea de cdigo, es sencillo configurar los valores de los parmetros mediante programacin. Todo lo que necesitamos hacer es crear un controlador de eventos para el evento Selecting del ObjectDataSource, el cual se genera antes que el mtodo del objeto subyacente es invocado y establecemos manualmente los valores para uno o ms parmetros por medio de la coleccin InputParameters. Este tutorial finaliza la seccin de Reporte Bsico. En el siguiente tutorial se inicia el filtrado y la seccin de los escenarios Maestro-Detalle, en la cual veremos tcnicas para
permitir al visitante filtrar los datos y profundizar a partir de un informe maestro en un informe de detalles.
MAESTRO / DETALLE
maestro/detalle. Durante este y los prximos tres tutoriales veremos una variedad de informes maestro/detalle. En este tutorial veremos cmo mostrar los registros maestros en un control DropDownList y los detalles de elemento de la lista seleccionado en un control GridView. Este informe maestro/detalle en particular muestra una lista de categoras e informacin de los productos. Paso 1: Visualizacin de las categoras en un DropDownList Nuestro informe maestro/detalle muestra una lista de categoras en un DropDownList, con los productos del elemento de la lista seleccionado ms abajo de la pagina en un GridView. Entonces la primera tarea que tenemos por delante es hacer que las categoras aparezcan en un DropDownList. Abra la pgina [Link] de la carpeta Filtering, arrastre un DropDownList de la caja de herramientas al diseador de la pagina y establezca su propiedad ID en Categories. Luego haga clic en el enlace Elegir Origen de datos en la etiqueta inteligente del DropDownList. Esto mostrara el asistente de configuracin de origen de datos.
Figura 1. Especificar el origen de datos del DropDownList Seleccione agregar un nuevo ObjectDataSource llamado CategoriesDataSource que invoque el mtodo GetCategories () de la clase CategoriesBLL.
Figura 4. Configuracin del ObjectDataSource para que utilice el mtodo GetCategories Despus de configurar el ObjectDataSource tenemos que especificar qu campo del origen de datos se debe mostrar en el DropDownList y cual debe estar asociado como
el valor del elemento de la lista seleccionado. Haga de CategoryName el campo para mostrar y CategoryID el valor de cada elemento de la lista.
Figura 5. Haga el campo CategoryName para mostrar y use CategoryID como el valor del DropDownList Hasta este momento tenemos un control DropDownList poblado con los registros de la tabla Categories (todo se logro en seis segundos). La figura 6 muestra nuestro proceso hasta ahora visto a travs de un navegador.
Paso 2: Agregar el GridView Productos El ltimo paso de nuestro informe maestro/detalle es mostrar los productos asociados con la categora seleccionada. Para realizar esto, agregue un GridView a la pgina y agregue un nuevo ObjectDataSource llamado ProductsDataSource. Haga que el control ProductsDataSource tome sus datos del mtodo GetProductsByCategoryID (categoryID) de la clase productsBLL.
Figura 7. Seleccione el mtodo GetProductsByCategoryID (categoryID) Despus de elegir este mtodo, el asistente del ObjectDataSource nos solicita el valor para el parmetro categoryID del mtodo. Para utilizar el valor de los elementos del DropDownList Categories establezca el origen de los parmetros en Control y el ControlID en Categories.
Figura 8. Establezca el parmetro CategoryID con el valor del DropDownList Categories Tome un momento para ver nuestro progreso a travs del navegador. La primera vez que visite la pgina, se muestran los productos pertenecientes a la categora seleccionada (bebidas) como se observa en la figura 9, pero al cambiar el DropDownList no se actualizan los datos. Esto se debe a que para que el GridView se actualice debe ocurrir una devolucin de datos. Para realizar esto, tenemos dos opciones (ninguna de ellas requiere escribir cdigo):
Figura 9. Cuando se visita por primera vez la pgina se muestran los productos de Bebidas
Figura 10. Seleccionar Produce genera automticamente un PostBack y actualiza el GridView Agregue un Seleccione una categora a la lista de elementos
La primera vez que visite la pgina [Link], el primer elemento de la lista del DropDownList Categories (bebidas) est seleccionado por defecto, mostrando los productos de bebidas en el GridView. En lugar de mostrar los productos de la primera categora, podramos querer tener seleccionado un elemento de la lista del DropDownList que diga algo as como: -Seleccione una categora-. Para agregar un nuevo elemento a la lista del DropDownList, vaya a la ventana propiedades y haga clic en los puntos suspensivos de la propiedad Items. Agregue un nuevo elemento de la lista con el Text -Elija una categora- y el Value -1.
Figura 11. Agregue un elemento -Seleccione una categora- a la lista De forma alternativa, usted puede agregar el elemento a la lista aadiendo el siguiente marcado de cdigo al control DropDownList:
<asp:DropDownList ID="categories" runat="server" AutoPostBack="True" DataSourceID="categoriesDataSource" DataTextField="CategoryName" DataValueField="CategoryID" EnableViewState="False"> <asp:ListItem Value="-1"> -- Choose a Category </asp:ListItem> </asp:DropDownList>
Adems necesitamos establecer el AppendDataBoundItems del DropDownList en True, porque cuando las categoras son enlazadas al DropDownList desde el ObjectDataSource sobrescriben cualquier elemento agregado de forma manual a la lista si AppendDataBoundItems es False
Figura 12. Establezca la propiedad AppendDataBoundItems en True Despus de estos cambios, la primera vez que visite la pgina esta seleccionada la opcin -Seleccione una categora- y no se muestra ningn producto.
Figura 13. En la pgina inicialmente no se carga ningn producto La razn por la que no se muestra ningn producto cuando esta seleccionada la opcin -Seleccione una categora- se debe a que su valor es -1 y no existe ningn producto en la base de datos cuyo CategoryID sea -1. Si este es el comportamiento que desea, entonces en este momento ha terminado, sin embargo, si desea visualizar los
productos de todas las categoras cuando el elemento de la lista -Seleccione una categora- es seleccionado, regrese a la clase ProductsBLL y personalice el mtodo GetProductsByCategoryID (categoryID) para que invoque el mtodo GetProducts () si el parmetro CategoryID que se pase es menor a cero:
Public Function GetProductsByCategoryID(categoryID As Integer) _ As [Link] If categoryID < 0 Then Return GetProducts() Else Return [Link](categoryID) End If End Function
La tcnica utilizada aqu es similar al enfoque empleado para mostrar todos los proveedores en el tutorial Declaracin de Parmetros, aunque para este ejemplo estamos usando un valor de -1 en lugar de Null para indicar que se deben recuperar todos los productos. Esto es porque el parmetro categoryID del mtodo GetProductsByCategoryID (categoryID) espera que se le pase un valor entero, mientras que en el tutorial de Declaracin de Parmetros se tena un parmetro de entrada que esperaba que se le pasara una cadena. La figura 14 muestra una captura de pantalla de [Link] cuando la opcin -Seleccione una categora- esta seleccionada. Aqu se muestran de forma predeterminada todos los productos y el usuario puede reducir la lista seleccionando una categora especfica.
Figura 14. Ahora aparecen todos los productos de forma predeterminada Resumen Al mostrar los datos relacionados jerrquicamente, a menudo ayuda presentar los datos utilizando informes maestro/detalle desde los cuales el usuario puede empezar a leer los datos desde la parte alta de la jerarqua y luego profundizar en los detalles. En este tutorial examinamos la construccin de un informe maestro/detalle simple que muestra los productos de una categora seleccionada. Esto se logr mediante un DropDownList para mostrar las categoras y un GridView para mostrar los productos que pertenecen a la categora seleccionada.
cliente/pedido/detalle de pedido en sus tablas Customers, Orders, Order Details, estas tablas no son capturadas en nuestra arquitectura. Sin embargo podemos ilustrarlo con dos DropDownList dependientes. El primer DropDownList muestra las categoras y el segundo muestra los productos que pertenecen a la categora seleccionada Luego el DetailsView recoge los detalles del producto seleccionado. Paso 1: Crear y poblar el control DropDownList de las categoras Nuestro primer objetivo es agregar un DropDownList que muestre las categoras. Estos pasos fueron examinados con detalle en el tutorial anterior, pero los resumiremos aqu para realizar el proceso. Abra la pgina [Link] de la carpeta Filtering, agregue un DropDownList a la pgina, configure su propiedad ID en Categories y luego haga clic en el enlace configurar origen de datos en su etiqueta inteligente. Desde el asistente de configuracin de origen de datos seleccione agregar un nuevo origen de datos.
Figura 1. Agregue un nuevo origen de datos al DropDownList El nuevo origen de datos debe ser naturalmente un ObjectDataSource. Llame este nuevo ObjectDataSource CategoriesDataSource y haga que el objeto invoque el mtodo GetCategories () de la clase CategoriesBLL.
Figura 3. Configure el ObjectDataSource para que utilice el mtodo GetCategories () Despus de configurar el ObjectDataSource, aun tenemos que definir qu campo del origen de datos se debe mostrar en el DropDownList Categories y cual debe configurarse como el valor de los elementos de la lista. Establezca CategoryName como el campo para mostrar y CategoryID como el valor de cada elemento de la lista.
Figura 4. Haga que el DropDownList muestre el campo CategoryName y use como valor el CategoryID
Hasta este momento tenemos un control DropDownList (Categories) poblado con los registros de la tabla Categories. Queremos que cuando el usuario escoja una nueva categora del DropDownList se produzca una devolucin de datos con el fin de que se actualice el DropDownList Products que vamos a crear en el paso 2. Por lo tanto seleccione la opcin Habilitar AutoPostBack en la etiqueta inteligente del DropDownList Categories.
Figura 5. Habilite AutoPostBack del DropDownList Categories Paso 2: Visualizacin de los productos de la categora seleccionada en un segundo DropDownList Con el DropDownList Categories terminado, nuestro siguiente paso es mostrar un DropDownList de los productos que pertenecen a la categora seleccionada. Para ello, agregue otro DropDownList a la pgina denominado ProductsByCategory. Al igual que con el DropDownList Categories, cree un nuevo ObjectDataSource para el DropDownList ProductsByCategory llamado ProductsByCategoryDataSource.
Figura 7. Crear un nuevo ObjectDataSource llamado ProductsByCategoryDataSource Como el DropDownList necesita mostrar solamente los productos que pertenecen a la categora seleccionada, haga que el ObjectDataSource invoque el mtodo GetProductsByCategoryID (categoryID) de la clase ProductsBLL.
Figura 9. Configure el ObjectDataSource para que utilice el mtodo GetProductsByCategoryID (categoryID) En el ltimo paso del asistente es necesario especificar el valor del parmetro CategoryID. Asigne este parmetro al elemento seleccionado de la lista del DropDownList Categories.
Figura 10. Tome el valor del parmetro categoryID del DropDownList Categories Con el ObjectDataSource configurado, todo lo que queda es especificar los campos del origen de datos que se utilizaran para mostrar y como valor de los elementos de la lista del DropDownList. Utilice el campo ProductName para mostrar y ProductID como valor.
Figura 11. Especifique los campos del origen de datos usado por las propiedades Text y Value del ListItem del control DropDownList Con el ObjectDataSource y el DropDownList ProductsByCategory configurados, la pgina mostrar dos DropDownList: el primero muestra las categoras, mientras que el segundo muestra los productos que pertenecen a la categora seleccionada. En primer lugar cuando el usuario selecciona una categora, se produce una devolucin de datos y el segundo DropDownList se actualiza, mostrando los productos que pertenecen a la categora que acaba de seleccionar. Las figuras 12 y 13 muestran la pgina [Link] en accin, vista a travs de un navegador.
Figura 12. Cuando visite por primera vez la pgina, la categora bebidas esta seleccionada
Figura 13. Seleccionando una nueva categora vemos los productos de la nueva categora Actualmente cuando se cambia el DropDownList ProductsByCategory, no se produce ninguna devolucin de datos. Sin embargo queremos que se produzca una devolucin de datos una vez agreguemos el DetailsView para mostrar los detalles del producto seleccionado (paso 3). Por lo tanto verifique que la opcin Habilitar AutoPostBack en la etiqueta inteligente del DropDownList ProductsByCategory este seleccionada.
Paso 3: Utilice un DetailsView para mostrar los detalles del producto seleccionado El paso final consiste en mostrar los detalles del producto seleccionado en un DetailsView. Para realizar esto, agregue un DetailsView a la pgina, configure su propiedad ID en ProductDetails y utilice un nuevo ObjectDataSource. Configure este ObjectDataSource para que tome sus datos del mtodo GetProductByProductID (productID) de la clase ProductsBLL que utilice el valor seleccionado en el DropDownList ProductsByCategory como el valor del parmetro productID.
Figura 16. Configure el ObjectDataSource para que utilice el mtodo GetProductByProductID (productID)
Figura 17. Tome el valor del parmetro productID del DropDownList ProductsByCategory Usted puede optar por mostrar cualquiera de los campos disponibles en el DetailsView ProductDetails. Hemos optado por quitar los campos ProductID, SupplierID y CategoriesID y reordenar y dar formato a los campos restantes. Adems limpiamos las propiedades Height y Width, permitiendo que el DetailsView ample el ancho necesario
para mostrar mejor los datos, en lugar de obligarlo a tener un tamao especfico. El marcado completo aparece a continuacin:
<asp:DetailsView ID="ProductDetails" runat="server" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" EnableViewState="False"> <Fields> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True" SortExpression="SupplierName" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" /> <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock" SortExpression="Units In Stock" /> <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder" SortExpression="Units On Order" /> <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel" SortExpression="Reorder Level" /> <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued" /> </Fields> </asp:DetailsView>
Tmese un momento para probar la pgina [Link] en un navegador. A primera vista puede parecer que todo funciona como queremos, pero hay un problema sutil. Cuando se elige una nueva categora, el DropDownList ProductsByCategory se actualiza para incluir los productos de la categora seleccionada, pero el DetailsView ProductDetails continua mostrando la informacin del producto anterior. El DetailsView es actualizado cuando seleccionamos un producto diferente para la categora seleccionada. Por otra parte si bien la prueba es suficiente, usted encontrar que si elige continuamente nuevas categoras (como la eleccin de bebidas
del DropDownList Categories, luego condimentos. Luego confites) cada nueva seleccin de otra categora hace que el DetailsView ProductDetails se actualice. Para ayudar a concretar este problema, veamos un ejemplo especfico. La primera vez que visite la pgina, la categora bebidas ha sido seleccionada y los productos relacionados con ella son cargados al DropDownList ProductsByCategory. Chai es el producto seleccionado y sus detalles se muestran en el DetailsView ProductDetails como se observa en la figura 18.
Figura 18. Los detalles del producto seleccionado son mostrados en un DetailsView Si cambia la categora seleccionada bebidas por condimentos, se produce una devolucin Chai. de datos y en consecuencia se actualiza el DropDownList PorductsByCategory, pero el DetailsView sigue mostrando los detalles del Producto
Figura 19. Los datos del producto seleccionado antes continan vindose Escoger un nuevo producto de la lista actualiza el DetailsView como esperbamos. Si selecciona una categora despus de cambiar el producto, una vez ms el DetailsView no se actualizar. Sin embargo si en vez de escoger un nuevo producto, usted selecciona una nueva categora; El DetailsView volver a actualizarse, entonces Qu diablos est pasando aqu? El problema es un error de tiempo en el ciclo de vida de la pgina. Cada vez que se solicita una pgina se procede a travs de una serie de medidas para su presentacin. En uno de estos pasos, los controles ObjectDataSource verifican para ver si alguno de sus valores SelectParameters ha cambiado. Si es as, el control de datos web enlazado al ObjectDataSource sabe que necesita actualizar su pantalla. Por ejemplo cuando se selecciona una nueva categora, el ObjectDataSource ProductsByCategoryDataSource detecta que los valores de sus parmetros han cambiado y vuelve a enlazar el DropDownList, obteniendo los productos de la categora seleccionada. El problema que se plantea en esta situacin es que el punto en el ciclo de vida de la pgina en el que los ObjectDataSource verifican el cambio de sus parmetros se produce antes que se re consoliden los datos asociados a los controles web. Por lo tanto al seleccionar una nueva categora el ObjectDataSource ProductsByCategoryDataSource detecta un cambio en el valor de su parmetro. Sin embargo el ObjectDataSource usado por el DetailsView ProductDetails no nota ninguno
de estos cambios porque el DropDownList ProductsByCategory no ha sido re enlazado aun. Despus en el ciclo de vida de la pgina el DropDownList ProductsByCategory vuelve a enlazar su ObjectDataSource tomando los productos para la nueva categora seleccionada. Mientras que el valor del DropDownList ProductsByCategory ha cambiado, el ObjectDataSource del DetailsView ProductDetails ya ha hecho su verificacin de valores de parmetros, por lo tanto el DetailsView, muestra los resultados anteriores. Esta interaccin se muestra en la figura 20.
Figura 20. El valor del DropDownList ProductsByCategory cambia luego que el ObjectDataSource del DetailsView ProductDetails verifica lo cambios Para remediar esta situacin, tenemos que volver a vincular el DetailsView ProductDetails explcitamente luego que el DropDownList ProductsByCategory se haya consolidado. Podemos realizar esto llamando al mtodo DataBind () del DetailsView ProductDetails cuando se genere el evento DataBound del DropDownList ProductsByCategory. Agregue el siguiente cdigo de marcado al controlador de eventos de la clase subyacente de la pgina de cdigo de [Link] (Refirase a Establecer eventos):
Protected Sub ProductsByCategory_DataBound(sender As Object, e As EventArgs) _ Handles [Link] [Link]() End Sub
mediante
programacin
los
valores
de
los
parmetros
del
Despus de agregar esta llamada explicita al mtodo DataBind () del DetailsView ProductDetails, el tutorial funciona como esperbamos. La figura 21 muestra como este cambio remedia nuestro anterior problema.
Figura 21. El DetailsView ProductDetails es explcitamente actualizado cuando se emite el evento DataBound del DropDownList ProductsByCategory Resumen El DropDownList sirve como un elemento de interfaz de usuario ideal para informes maestro/detalle donde existe una relacin uno a varios entre los registros maestros y detalles. En el tutorial anterior vimos como utilizar un solo DropDownList para filtrar los productos que pertenecen a la categora seleccionada. En este tutorial hemos remplazado el GridView con un DropDownList de productos y utilizamos un DetailsView para mostrar los detalles del producto seleccionado. Los conceptos discutidos en este tutorial se pueden ampliar fcilmente a los modelos de datos relacionados con varias relaciones uno a varios, tales como los clientes, pedidos y artculos ordenados. En general usted siempre puede agregar a un DropDownList para cada una de las entidades de las relaciones uno a varios.
Figura 1. Agregar las pginas [Link] y [Link] a la carpeta Filtering Adems al aadir las pginas asegrese de actualizar el archivo del mapa del sitio [Link] correctamente. Para este tutorial solo tenemos que aadir la pgina [Link] al mapa del sitio, usando el siguiente contenido XML como un hijo del elemento <SiteMapNode> Filtering Reports:
<siteMapNode url="/Filtering/[Link]" title="Master/Detail Across Two Pages" description="Master records on one page, detail records on another." />
Nota: Usted puede ayudar a automatizar el proceso de actualizacin del archivo del mapa del sitio al aadir nuevas pginas [Link] usando gratis el SiteMapMacro de Visual Studio de K. Scott Allen. Paso 2: Visualizacin de la lista de proveedores [Link] Con las pginas [Link] y [Link] creadas, nuestro siguiente paso es crear el GridView de los proveedores en [Link]. Aada un control GridView a la pgina y enlcelo a un nuevo ObjectDataSource. Este ObjectDataSource debe utilizar el mtodo GetSuppliers () de la clase SuppliersBLL para devolver todos los proveedores.
Figura 3. Configure el ObjectDataSource para que use el mtodo GetSuppliers () Debemos incluir un enlace titulado Ver Productos en cada dila del GridView, para que cuando se haga clic lleve al usuario a [Link] pasando el valor de SupplierID de la fila seleccionada empleando una cadena de consulta. Por ejemplo si el usuario hace clic en el enlace Ver Productos de Comerciantes de Tokio (que tiene un valor de SupplierID de 4), deben ser enviados a [Link]?SupplierID=4. Para lograr esto, agregue un HiperLinkField al GridView, el cual agrega un hipervnculo a cada fila del GridView. Comience haciendo clic en el enlace Editar columnas de la etiqueta inteligente del GridView. Luego seleccione el HiperLinkField de la lista en la
parte superior izquierda y haga clic en agregar para incluir el HiperLinkField en la lista de campos del GridView.
Figura 4. Agregar un HiperLinkField al GridView El HiperLinkFied puede ser configurado para utilizar el mismo texto o valores Url del link en cada fila del GridView, o puede basar estos valores en los valores de datos enlazados de cada fila particular. Para especificar un valor esttico en todas las filas utilice las propiedades Text o NavigateUrl del HiperLinkFied. Como queremos que el texto del enlace sea el mismo para todas las filas, establezca la propiedad Text del HiperLinkField en Ver Productos.
Para establecer el texto o los valores Url basados en los datos subyacentes enlazados a la fila del GridView, especificamos los campos de datos, texto o valores Url que deben ser sacados de las propiedades DataTextField o DataNavigateUrlFields. El DataTextField solo puede establecerse como un nico campo de datos, sin embargo, DataNavigateUrlFields puede establecer una lista de campos de datos delimitada por comas. Con frecuencia necesitamos basar el texto o la direccin Url en una combinacin del valor del campo de datos de la fila actual y algn marcado esttico. En este tutorial por ejemplo, queremos que la direccin de los vnculos del HiperLinkField sean [Link]?SupplierID=supplierID, donde supplierID es el valor de SupplierID de cada fila del GridView. Tenga en cuenta que necesitamos tanto el marcado esttico como los valores de datos impulsados aqu: la parte del Url del Link [Link]?SupplierID= fila. Para indicar una combinacin de valores estticos y datos impulsados, utilice las propiedades el valor DataTextFormatString del campo y DataNavigateUrlFormatString. en las propiedades En estas o propiedades ingrese el marcado esttico necesario y utilice el marcador {0} si desea que especificado DataTextField DataNavigateUrlFields aparezca. Si la propiedad DataNavigateUrlFields cuenta con varios campos especificados, use {0} si desea el valor del primer campo insertado, {1} para el valor del segundo campo y as sucesivamente. Aplicando esto a nuestro tutorial, tenemos que establecer la propiedad es esttica, mientras que la parte supplierID es el valor impulsado ya que su valor es el valor SupplierID propio de cada
DataNavigateUrlFields a SupplierID, ya que es el campo de datos cuyo valor necesitamos especificar para cada fila y la propiedad DataNavigateUrlFormatString en [Link]?SupplierID={0}.
Figura 6. Configure el HiperLinkField para incluir el hipervnculo adecuado, basndose en el SupplierID Despus de aadir el HiperLinkField, no dude en personalizar y ordenar los campos del GridView. El siguiente marcado muestra el control GridView despus de que se la han hecho algunas modificaciones menores a nivel de campo.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="SupplierID" DataSourceID="ObjectDataSource1" EnableViewState="False"> <Columns> <asp:HyperLinkField DataNavigateUrlFields="SupplierID" DataNavigateUrlFormatString= "[Link]?SupplierID={0}" Text="View Products" /> <asp:BoundField DataField="CompanyName" HeaderText="Company" SortExpression="CompanyName" /> <asp:BoundField DataField="City" HeaderText="City" SortExpression="City" /> <asp:BoundField DataField="Country" HeaderText="Country" SortExpression="Country" /> </Columns> </asp:GridView>
Tmese un momento para ver la pgina [Link] en un navegador. La figura 7 muestra actualmente la lista de todos los proveedores incluyendo un enlace Ver Productos. Al hacer clic en uno de los enlaces Ver Productos nos llevar a
Figura 7. Cada fila de Proveedor contiene un enlace Ver Productos Paso 3: Mostrar los productos de los proveedores en la pgina
[Link] Hasta el momento la pgina [Link] est enviando a los usuarios a [Link] pasando el SupplierID del proveedor seleccionado a travs de una cadena de consulta. El paso final del tutorial es mostrar los productos en un GridView en la pgina [Link] cuyo SupplierID es igual al SupplierID pasada a travs de la cadena de consulta. Para realizar esto, comience agregando un GridView a la pgina [Link], con un nuevo control ObjectDataSource llamado ProductsBySupplierDataSource que invoque el mtodo GetProductsBySupplierID (supplierID) de la clase ProductsBLL.
Figura 10. Haga que el ObjectDataSource invoque el mtodo GetProductsBySupplierID (supplierID) El paso final del asistente de configuracin de origen de datos nos solicita que proporcionemos el origen del parmetro supplierID del mtodo GetProductsBySupplierID (supplierID). Para utilizar el valor de la cadena de consulta establecemos el origen del parmetro en cadena de consulta e introducimos el nombre del valor de la cadena de consulta que utilizaremos en el cuadro de texto QueryStringField (SupplierID).
Figura 11. Poblar el valor del parmetro supplierID desde el valor de la cadena de consulta SupplierID Eso es todo lo que hay que hacer! La figura 12 muestra la pgina
[Link] cuando la visitamos haciendo clic en el enlace Comerciantes de Tokio desde la pgina [Link].
Figura 12. Se muestran los productos suministrados por Comerciantes de Tokio Mostrar la informacin del proveedor en [Link] En la figura 12 se observa que la pgina [Link] se limita a enumerar los productos que son suministrados por el SupplierID especificado en la cadena de consulta. Sin embargo, si alguien es enviado directamente a esta pgina no
sabra que los productos que se muestran en la Figura 12 son los productos proporcionados por los Comerciantes de Tokio. Para remediar esto podemos mostrar tambin la informacin del proveedor. Comencemos agregando un FormView encima del GridView de los productos. Cree un nuevo control ObjectDataSource llamado SuppliersDataSource que invoque el mtodo GetSupplierBySupplierID (supplierID) de la clase SuppliersBLL.
Figura 14. Haga que el ObjectDataSource invoque el mtodo GetSupplierBySupplierID (supplierID) Al igual que con el ProductsBySupplierDataSource, tome el parmetro supplierID asignado al valor SupplierID de la cadena de consulta.
Figura 15. Rellene el valor del parmetro SupplierID con el valor SupplierID de la cadena de consulta
Cuando enlazamos el FormView al ObjectDataSource en la vista diseo, Visual Studio crea automticamente el ItemTemplate, InsertTemplate y EditItemTemplate con controles Label y controles TextBox para cada uno de los campos devueltos por el ObjectDataSource. Puesto que solo queremos mostrar la informacin del Proveedor no dude en retirar el InsertTemplate y el EditItemTemplate. Luego modifique la propiedad ItemTemplate para mostrar el nombre de la compaa del proveedor en un elemento <h3> y la direccin, ciudad, pas y nmero de telfono debajo del nombre de la empresa. Si lo prefiere puede configurar manualmente el DataSourceID del FormView y crear el marcado del ItemTemplate, como lo hicimos en el tutorial Mostrar datos con el ObjectDataSource. Luego de estas modificaciones, el marcado declarativo del FormView debe ser similar al siguiente:
<asp:FormView ID="FormView1" runat="server" DataKeyNames="SupplierID" DataSourceID="suppliersDataSource" EnableViewState="False"> <ItemTemplate> <h3><%# Eval("CompanyName") %></h3> <p> <asp:Label ID="AddressLabel" runat="server" Text='<%# Bind("Address")%>'> </asp:Label> <br/> <asp:Label ID="CityLabel" runat="server" Text='<%# Bind("City") %>'> </asp:Label>, <asp:Label ID="CountryLabel" runat="server" Text='<%# Bind("Country") %>'></asp:Label><br /> Phone: <asp:Label ID="PhoneLabel" runat="server" Text='<%# Bind("Phone") %>'></asp:Label> </p> </ItemTemplate> </asp:FormView>
La
Figura
16
muestra
una
captura
de
pantalla
de
la
pgina
Figura 16. La lista de productos incluye un resumen del Proveedor Aplicacin de toques finales para la interfaz de usuario
[Link] Para mejorar la experiencia del usuario de este informe, hay un par de adiciones que debemos hacer a la pgina [Link]. Actualmente la nica manera que un usuario pueda pasar de la pgina [Link] a la lista de proveedores es hacer clic en el botn atrs de su navegador. Agregaremos un control HiperLink a la pgina [Link] que enlazara nuevamente a [Link], proporcionando otra manera de que el usuario regrese a la lista principal.
Figura 17. Agregue un control HiperlinkField para que el usuario regrese a la [Link]
Si el usuario hace clic en el enlace Ver los Productos de un proveedor que no tiene ningn tipo de productos, el ObjectDataSource ProductsBySupplierDataSource en la pgina [Link] no devolver ningn resultado. El GridView enlazado al ObjectDataSource no tendr ningn marcado resultando una regin en blanco en la pgina del navegador del usuario. Para comunicarle con mayor claridad al usuario que no hay productos asociados con el proveedor seleccionado, podemos establecer la propiedad EmptyDataText del GridView para que aparezca un mensaje que queramos cuando ocurra una situacin as. En esta propiedad se ha establecido No hay productos ofrecidos por este proveedor. Por defecto todos los proveedores de la base de datos Northwind proporcionan por lo menos un producto. Sin embargo para este tutorial hemos modificado la tabla Products para que el proveedor Escargots Nouveaux ya no est asociado con ningn producto. La Figura 18 muestra la pgina de detalles de Escargots Nouveaux despus de que sea realizado este cambio.
Figura 18. Los usuarios son informados que el proveedor no proporciona ningn producto Resumen Aunque los informes Maestro/Detalle pueden mostrar tanto los registros maestro y detalle en una sola pgina, en la mayora de sitios web se separan en dos pginas web. En este tutorial vimos como implementar un informe Maestro/Detalle teniendo los
proveedores listados en un GridView en la pgina maestro y los productos asociados mostrados en la pgina detalles. Cada fila de proveedores en la pgina web principal contiene un enlace a la pgina de detalles que pasan el valor SupplierID de la fila. Tales vnculos especficos de las filas se pueden agregar fcilmente utilizando HiperLinkField en el GridView. En la pgina detalles la recuperacin de los productos para el proveedor especificado se llevo a cabo mediante la invocacin del mtodo GetProductsBySupplierID (supplierID) de la clase ProductsBLL. El valor del parmetro SupplierID se especifica mediante declaracin utilizando una cadena de consulta como el origen de los parmetros. Tambin nos fijamos en la forma de mostrar los detalles del proveedor en la pgina detalles usando un FormView. Nuestro siguiente tutorial es el ltimo de reportes maestro/detalles. Veremos cmo mostrar una lista de Productos en un GridView donde cada fila tiene un botn de seleccin. Al hacer clic en el botn seleccionar mostrar los detalles de ese producto en un control DetailsView en la misma pgina.
10.
Figura 1. Al dar clic en el botn Select mostramos los detalles del producto Paso 1: Crear un GridView Seleccionable
Recodemos que en el reporte maestro/detalle de dos pginas, cada registro maestro incluye un hipervnculo que cuando se hace clic sobre l, enva al usuario a la pgina de detalles pasando el valor del SupplierID de la fila seleccionada por medio de una cadena de consulta. El hipervnculo fue agregado a cada fila del GridView usando un HiperLinkField. Para un reporte maestro/detalle de una sola pgina, necesitaremos un Button para cada fila del GridView de forma que cuando sea presionado, muestre los detalles. El control GridView puede ser configurado para que incluya un botn Select para cada fila que origine una devolucin de datos y marque la fila para el SelectedRow del GridView. Comencemos agregando un control GridView a la pgina [Link] de la carpeta Filtering, estableciendo su propiedad ID en ProductsGrid. Luego agregamos un nuevo ObjectDataSource llamado AllProductsDataSource que invoque el mtodo GetProducts () de la clase ProductsBLL.
Figura 4. Configure el ObjectDataSource para que invoque el mtodo GetProducts () Edite los campos del GridView removiendo todos los BoundFields a excepcin del BoundField ProductName y UnitPrice. Tambin sintase libre de personalizar estos BoundFields segn sea necesario, como por ejemplo dar formato al BoundField UnitPrice como moneda y cambiar las propiedades HeaderText de los BoundFields. Estos pasos pueden realizarse grficamente, haciendo clic en la opcin editar columnas en la etiqueta inteligente del GridView, o configurando manualmente la sintaxis declarativa.
Figura 5. Remover todos los BoundFields a excepcin de ProductName y UnitPrice El marcado final declarativo es el siguiente:
<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="AllProductsDataSource" EnableViewState="False"> <Columns> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Unit Price" HtmlEncode="False" SortExpression="UnitPrice" /> </Columns> </asp:GridView>
Luego es necesario marcar el GridView como seleccionable, por lo cual necesitamos agregar un botn select a cada fila. Para realizar esto, simplemente seleccionamos la casilla Habilitar Seleccin en la etiqueta inteligente del GridView.
Figura 6. Marque como seleccionable las filas del GridView Seleccionando la opcin Habilitar Seleccin agregamos un CommandField al GridView con su propiedad ShowSelectButton establecida en True. Esto resulta en un botn Select para cada fila del GridView como se muestra en la figura 6. De forma predeterminada los botones de seleccin se representan como LinkButtons pero pueden emplearse en su lugar buttons o ImageButtons por medio de la propiedad ButtonType del CommandField.
<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="AllProductsDataSource" EnableViewState="False"> <Columns> <asp:CommandField ShowSelectButton="True" /> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Unit Price" HtmlEncode="False" SortExpression="UnitPrice" /> </Columns> </asp:GridView>
Cuando se selecciona el botn Select de una fila del GridView se produce una devolucin de datos y la propiedad SelectedRow se actualiza. Adems de la propiedad
SelectedRow,
el
GridView
proporciona
otras
propiedades
como
SelectedIndex,
SelectedValue y SelectedDataKey. La propiedad SelectedIndex devuelve el ndice de la fila seleccionada, mientras que las propiedades SelectedValue y SelectedDataKey devuelven los valores basados en la propiedad DataKeyNames del GridView. La propiedad DataKeyNames se utiliza para asociar uno o ms valores de los campos de datos con cada fila y se utiliza comnmente para el atributo que identifica unvocamente la informacin de los datos subyacentes de cada fila del GridView. La propiedad SelectedValue devuelve el valor del primer campo de datos del DataKeyNames seleccionado, en tanto que la propiedad SelectedDataKey devuelve el objeto DataKey de la fila seleccionada, que contiene todos los valores de los campos de datos claves especificados para esa fila. La propiedad DataKeyNames se establece automticamente en el campo que identifica unvocamente los datos cuando enlazamos un Data Source a un GridView, DetailsView o FormView a travs del diseador. Si bien esta propiedad se ha establecido de forma automtica por nosotros en los tutoriales anteriores, los ejemplos podran haber trabajado sin especificar la propiedad DataKeyNames. Sin embargo, para el GridView seleccionable de este tutorial as como para los futuros tutoriales que examinaremos en insercin, actualizacin y eliminacin, la propiedad DataKeyNames debe establecerse correctamente. Tmese su tiempo para verificar y asegurarse que la propiedad DataKeyNames de su GridView est establecida en ProductID. Veremos nuestro progreso hasta el momento a travs de un navegador. Tenga en cuenta que el control GridView muestra el nombre y el precio de todos los productos, junto con un LinkButton de seleccionar. Al hacer clic en el botn Seleccionar se produce una devolucin de datos. En el paso 2 veremos cmo tener un DetailsView que responda a esta devolucin de datos, mostrando los detalles del producto seleccionado.
Figura 7. Cada fila del producto contiene un LinkButton Seleccionar Resaltando la fila seleccionada El GridView ProductsGrid tiene una propiedad SelectedRowStyle que se puede utilizar para dictar el estilo visual de la fila seleccionada. Usado adecuadamente, este puede mejorar la experiencia del usuario de una forma ms clara mostrando la fila del GridView que actualmente est seleccionada. Para este tutorial tendremos que la fila seleccionada se resaltara con un fondo en amarillo. Al igual que con nuestros tutoriales anteriores, trataremos de mantener la configuracin relacionada con la apariencia definida como clases CSS. Por lo tanto crearemos una nueva clase en CSS [Link] llamada SelectedRowStyle.
.SelectedRowStyle { background-color: Yellow; }
Para aplicar esta clase CSS a la propiedad SelectedRowStyle de todos los GridView que se muestran en esta serie de tutoriales, editamos la capa [Link] en el tema DataWebControls para incluir la configuracin SelectedRowStyle como se muestra a continuacin:
<asp:GridView runat="server" CssClass="DataWebControlStyle"> <AlternatingRowStyle CssClass="AlternatingRowStyle" /> <RowStyle CssClass="RowStyle" /> <HeaderStyle CssClass="HeaderStyle" /> <SelectedRowStyle CssClass="SelectedRowStyle" />
</asp:GridView>
Con esta adicin, la fila seleccionada del GridView se resalta con un fondo de color amarillo.
Figura 8. Personalizar la apariencia de la fila seleccionada usando la propiedad SelectedRowStyle del GridView Paso 2: Mostrar la informacin del producto seleccionado en un DetailsView Con el GridView ProductsGrid completo, todo lo que nos queda es aadir un DetailsView que muestre la informacin en concreto del producto seleccionado. Agregue un control DetailsView por encima del GridView y cree un nuevo ObjectDataSource llamado ProductDetailsDataSource. Dado que queremos que este DetailsView muestre la informacin sobre el producto seleccionado en particular, configure el ProductDetailsDataSource para que utilice el mtodo GetProductByProductID (productID) de la clase ProductsBLL.
Figura 9. Invocamos el mtodo GetProductByProductID (productID) de la clase ProductsBLL Haga que el valor del parmetro productID obtenga su valor de la propiedad SelectedValue del control GridView. Como ya comentamos anteriormente, la propiedad SelectedValue del GridView devuelve el primer valor de datos clave para la fila seleccionada. Por lo tanto es imperativo que la propiedad DataKeyNames del GridView se establezca en ProductID, de modo que el valor ProductID de la fila seleccionada sea devuelto por SelectedValue.
Figura 10. Ajuste el parmetro ProductID a la propiedad SelectedValue del GridView Una vez que el ObjectDataSource ProductDetailsDataSource se ha configurado y enlazado al DetailsView correctamente, este tutorial esta completo. Cuando se visita la pgina por primera vez, ninguna fila esta seleccionada, por lo cual la propiedad SelectedValue del GridView devuelve Nothing. Como no existen productos con un valor de ProductID de Null, no hay registros devueltos por el mtodo GetProductByProductID (productID) lo que significa que el DetailsView no se muestra (Ver Figura 11). Al hacer clic un el botn seleccionar de una fila del GridView, se produce una devolucin de datos y se actualiza el DetailsView. Esta vez la propiedad SelectedValue devuelve el ProductID de la fila seleccionada, el mtodo GetProductByProductID (productID) devuelve un ProductsDataTable con informacin acerca de ese producto en particular, y el DetailsView muestra los detalles (Ver Figura 12).
Figura 11. Cuando visitamos la pgina por primera vez se muestra el GridView
Figura 12. Al seleccionar una fila se muestra la informacin del producto Resumen En este y en los ltimos tres tutoriales hemos visto una serie de tcnicas para la visualizacin de informes maestro/detalle. En este tutorial examinamos un GridView seleccionable como hogar de los registros maestros y un DetailsView para visualizar los detalles del registro maestro seleccionado en la misma pgina. En los tutoriales anteriores vimos como mostrar informes maestro/detalle por medio de DropDownList y mostrar los registros maestros en una pgina y los detalles en otra. En este tutorial concluimos muestro examen de los informes maestro/detalles. Iniciamos en el siguiente tutorial la exploracin del formato personalizado en los controles GridView, FormView y DetailsView. Veremos cmo personalizar el formato de estos controles, basados en los datos enlazados a ellos, as como resumir datos en el pie de pgina de un GridView y cmo utilizar las plantillas para obtener un mayor grado de control sobre el diseo.
FORMATO PERSONALIZADO
11.
Ajustar el formato de los controles GridView, FormView y DetailsView basado en los datos enlazados a ellos se puede lograr de diferentes formas. En este tutorial veremos cmo realizar el formato de los datos enlazados por medio del uso de los controladores de eventos del RowDataBound y DataBound. Introduccin La apariencia de los controles GridView, FormView y DetailsView puede ser personalizada por medio de un gran nmero de propiedades relacionadas con el estilo. Propiedades como CssClass, Font, BorderWidth, BorderStyle, BorderColor, Width y Height, entre otros dictan el aspecto general del control. Las propiedades HeaderStyle, RowStyle, AlternatingRowStyle permiten establecer las mismas configuraciones de estilos para aplicarlos a secciones particulares. Del mismo modo estos ajustes de estilo pueden aplicarse a nivel de campo. Sin embargo en muchas situaciones los requisitos del formato dependen de los datos que se muestran. Por ejemplo para llamar la atencin sobre los productos fuera de existencia, podramos tener un reporte que muestre la informacin de los productos cuyo color de fondo sea amarillo para aquellos productos cuyos campos UnitsInStock y UnitsOnOrder son iguales a 0. Para destacar los productos ms costosos, es posible que deseemos mostrar los precios de los productos que cuestan ms de $75.00 en negrita. Ajustar el formato de los controles GridView, FormView y DetailsView basados en los datos enlazados a ellos puede realizarse de diferentes maneras. En este tutorial veremos cmo realizar el formato de los datos enlazados a travs del uso de los controladores de eventos del RowDataBound y DataBound. En el siguiente tutorial exploraremos un enfoque alternativo. Uso del controlador de eventos DataBound del control DetailsView Cuando los datos son enlazados a un DetailsView ya sea desde un control de origen de datos o mediante programacin por medio de la asignacin de datos a la propiedad DataSource del control y llamando a su mtodo DataBind (), se produce la siguiente secuencia de pasos:
1. Se desencadena el evento DataBinding del control de datos web 2. Los datos son enlazados al control de datos web 3. Se desencadena el evento DataBound del control de datos web La lgica personalizada puede ser ingresada inmediatamente despus de los pasos 1 y 3 por medio de un controlador de eventos. Al crear un controlador de eventos para el evento DataBound podemos determinar mediante programacin los datos que han sido enlazados al control de datos web y ajustar el formato si es necesario. Para ilustrar esto crearemos un DetailsView que muestre la informacin sobre un producto, pero que muestre el valor de UnitPrice en negrita, si este excede de 75.00 Paso 1: Visualizacin de la informacin del producto en un DetailsView Abra la pgina [Link] en la carpeta CustomFormatting, arrastre un control DetailsView desde el cuadro de herramientas al diseador, establezca el valor de su propiedad ID a ExpensiveProductsPriceInBoldItalic y enlcelo a un nuevo ObjectDataSource que invoque el mtodo GetProducts () de la clase ProductsBLL. Los pasos detallados para lograr esto se omiten aqu por brevedad ya que fueron examinados con detalle en los tutoriales anteriores. Una vez que haya enlazado el ObjectDataSource al DetailsView, tmese un momento para modificar la lista de campos. Hemos optado por quitar los BoundFields ProductID, SupplierID, CategoryID, UnitsInStock, UnitsOnOrder, ReorderLevel y Discontinued y cambie el nombre y el formato de los BoundFields restantes. Tambin limpiamos la configuracin Width y Heigth. Como el DetailsView muestra nicamente un solo registro, tenemos que habilitar la paginacin con el fin de permitirle al usuario ver todos los productos. Hgalo seleccionando la casilla Habilitar paginacin en la etiqueta inteligente del DetailsView.
Figura 1. Habilite la casilla Habilitar paginacin en la etiqueta inteligente del DetailsView Despus de estos cambios, el marcado del DetailsView ser el siguiente:
<asp:DetailsView ID="DetailsView1" runat="server" AllowPaging="True" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" EnableViewState="False"> <Fields> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="Supplier" ..ReadOnly="True" SortExpression="SupplierName" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" ..SortExpression="QuantityPerUnit" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" /> </Fields> </asp:DetailsView>
Figura 2. El control GridView muestra un producto a la vez Paso 2: Determinar mediante programacin el valor de los datos en el controlador de eventos DataBound Con el fin de mostrar el precio en negrita cursiva para aquellos productos cuyo valor de UnitPrice supera los $75.00, tenemos primero que ser capaz mediante programacin de establecer el valor de UnitPrice. Para el DetailsView, esto se puede lograr por medio del controlador de eventos DataBound. Para crear el controlador de eventos, haga clic en el diseador del DetailsView, luego vaya a la ventana propiedades. Presione F4 para que aparezca si no es visible o vaya al men ver y seleccione la opcin ventana de propiedades del men. En la ventana propiedades haga clic en el icono del rayo para mostrar los eventos del DetailsView. Luego haga doble clic en el evento DataBound o escriba el nombre del controlador de eventos que desea crear.
Figura 3. Crear un controlador de eventos para el evento DataBound Si lo hace crear automticamente el controlador de eventos y te lleva a la parte del cdigo en el que se ha aadido. En este punto veremos:
Protected Sub ExpensiveProductsPriceInBoldItalic_DataBound _ (sender As Object, e As [Link]) _ Handles [Link] End Sub
Nota: Tambin puede creer un controlador de eventos desde la porcin de cdigo de la pgina [Link]. All encontraras dos listas desplegables en la parte superior de la pgina. Seleccione el objeto en la lista desplegable de la izquierda y el evento en el que desea crear el controlador en la lista desplegable de la derecha y VisualStudio creara automticamente el controlador de eventos adecuado. Podemos acceder a los datos enlazados al DetailsView por medio de la propiedad DataItem. Recordemos que enlazamos nuestros controles a DataTables fuertemente tipiados, los cuales se componen de una coleccin de DataRow fuertemente tipiados. Cuando el DataTable es enlazado al DetailsView, el primer DataRow de la DataTable se asigna a la propiedad DataItem del DetailsView. En concreto la propiedad DataItem es asignada a un objeto DataRowView. Podemos utilizar la propiedad Row del DataRowView para tener acceso al objeto DataRow subyacente, que en realidad es una instancia ProductsRow. Una vez que tenemos esta instancia ProductsRow podemos
tomar nuestra decisin simplemente inspeccionando los valores de la propiedad del objeto. El siguiente cdigo muestra como determinar si el valor de UnitPrice enlazado al control DetailsView es mayor de $75,00:
Protected Sub ExpensiveProductsPriceInBoldItalic_DataBound _ (sender As Object, e As [Link]) _ Handles [Link] Dim product As [Link] = _ CType(CType([Link], _ [Link]).Row, [Link]) If Not [Link]() AndAlso [Link] > 75 Then End If End Sub
Nota: Como UnitPrice puede tener valores nulos en la base de datos, primero debemos asegurarnos que no estamos tratando con un valor Null antes de acceder a la propiedad UnitPrice del ProductsRows. Esta verificacin es importante ya que si intentamos acceder a la propiedad UnitPrice cuando tiene un valor Null, el objeto arrojara una excepcin StrongTypingException. Paso 3: Dar formato al valor de UnitPrice en el DetailsView Hasta el momento podemos determinar si el valor UnitPrice enlazado al DetailsView tiene un valor que supera los $75,00, pero todava no podemos ver como se modifica el formato del DetailsView mediante programacin. Para modificar el formato de una fila completa en el DetailsView accedemos mediante programacin a la fila usando [Link] (index) y para modificar una celda en particular accedemos usando [Link] (index).Cells (index). Una vez que tenemos referencia a la fila o a la celda podemos ajustar su apariencia mediante la definicin de sus propiedades relacionadas al estilo. Acceder a una fila mediante programacin requiere que usted conozca el ndice de la fila, el cual comienza en 0. El UnitPrice es la quinta fila en el DetailsView dndole un ndice de 4 y accedemos a este mediante programacin usando [Link] (4). Hasta el momento podramos tener que el
contenido de una fila entera aparezca en negrita y cursiva utilizando el siguiente cdigo:
[Link](4).[Link] = True [Link](4).[Link] = True
Sin embargo esto har que tanto la etiqueta (de precios) y el valor aparezcan en cursiva y negrita. Si deseamos que solo el valor aparezca en negrita y cursiva es necesario aplicar este formato a la segunda celda de la fila, lo cual se puede lograr de la siguiente forma:
[Link](4).Cells(1).[Link] = True [Link](4).Cells(1).[Link] = True
Hasta el momento en nuestros tutoriales hemos utilizado hojas de estilo para mantener una clara separacin entre lo que representa el marcado y la informacin relacionada con el estilo, en lugar de establecer las propiedades de estilo especificas como se muestra arriba utilizaremos en su lugar una clase CSS. Abra la hoja de estilos CSS [Link] y agregue una nueva clase denominada ExpensivePriceEmphasis con la siguiente definicin:
.ExpensivePriceEmphasis { font-weight: bold; font-style: italic; }
Luego en el controlador de eventos DataBound, establezca la propiedad de celda CssClass en ExpensivePriceEmphasis. El siguiente cdigo muestro el controlador de eventos DataBound en su totalidad:
Protected Sub ExpensiveProductsPriceInBoldItalic_DataBound _ (sender As Object, e As [Link]) _ Handles [Link] Dim product As [Link] = _ CType(CType([Link], _ [Link]).Row, [Link]) If Not [Link]() AndAlso [Link] > 75 Then [Link](4).Cells(1).CssClass = _ "ExpensivePriceEmphasis" End If
End Sub
Cuando vemos Chai, el cual tiene un precio menor de $75,00 este se muestra en una fuente normal (Ver Figura 4). Sin embargo cuando vemos Mishi Kobe Niku que tiene un precio de $97,00, el precio se muestra en cursiva y negrita (Ver Figura 5).
Figura 5. Los precios ms costosos se muestran en una fuente en negrita y cursiva Uso del controlador de Eventos DataBound del control FormView Los pasos para determinar los datos subyacentes enlazados al FormView, son idnticos a los del DetailsView, cree un controlador de eventos DataBound, convierta la propiedad DataItem al tipo de objeto apropiado enlazado al control y determine como
proceder. Sin embargo, el FormView y el DetailsView difieren en la forma de la apariencia de su interfaz de usuario cuando es actualizada. El FormView no contiene ningn BoundFields y por lo tanto carece de coleccin Rows. En cambio, un FormView se compone de plantillas, que pueden contener una mezcla de contenido HTML esttico, controles web y sintaxis de enlace de datos. Ajustar el estilo del FormView normalmente implica ajustar el estilo de uno o varios controles web en las plantillas del FormView. Para ilustrar esto, vamos a utilizar un FormView para mostrar los productos del ejemplo anterior, pero esta vez solo vamos a mostrar el nombre del producto y las unidades de existencia, con las unidades de existencia que sean menores de 10 apareciendo en rojo. Paso 4: Visualizacin de la informacin de productos en un FormView Agregue un FormView a la pgina [Link] debajo del DetailsView y establezca su propiedad ID en LowStockedProductsInRed. Enlace el FormView al ObjectDataSource creado en el paso anterior. Esto crear un ItemTemplate, EditItemTemplate e InsertItemTemplate para el FormView. Retire el EditItemTemplate y el InsertItemTemplate y simplifique el ItemTemplate para que incluya solamente los valores ProductName y UnitsInStock, cada uno con su respectivo nombre en un control Label. Al igual que con el DetailsView del ejemplo anterior, consulte tambin la casilla Habilitar Paginacin en la etiqueta inteligente del FormView. Despus de estas ediciones el marcado de su FormView debe lucir de la siguiente manera:
<asp:FormView ID="LowStockedProductsInRed" runat="server" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True" EnableViewState="False"> <ItemTemplate> <b>Product:</b> <asp:Label ID="ProductNameLabel" runat="server" Text='<%# Bind("ProductName") %>'> </asp:Label><br /> <b>Units In Stock:</b> <asp:Label ID="UnitsInStockLabel" runat="server"
HTML Esttico: el texto Producto y UnitsInStock junto con los elementos </br> y
<b>.
Controles web: Los dos controles label ProductNameLabel y UnitsInStockLabel. Sintaxis de enlace de datos: <%#Bind (ProductName)%> y <%#Bind
(UnitsInStock)%> que asigna los valores de estos campos a las propiedades de los controles Label.
Paso 5: Determinacin mediante programacin de los valores en el controlador de eventos DataBound Con el marcado del FormView completo, el siguiente paso es determinar mediante programacin si el valor de UnitsInStock en menor o igual a 10. Es se logra de la misma forma en el FormView que como se realizo con el DetailsView. Comience por crear un controlador de eventos para el evento DataBound del FormView.
En el controlador de eventos convertimos la propiedad DataItem en una instancia ProductsRow y determinamos si el valor UnitPrice es tal que tenemos que mostrar su fuente de color rojo.
Protected Sub LowStockedProductsInRed_DataBound _ (sender As Object, e As [Link]) _ Handles [Link] Dim product As [Link] = _ CType(CType([Link],[Link]).Row, _ [Link]) If Not [Link]() AndAlso [Link] <= 10 Then Dim unitsInStock As Label = _ CType([Link]("UnitsInStockLabel"), Label) If unitsInStock IsNot Nothing Then End If End If End Sub
Paso 6: Dar formato al control Label UnitsInStockLabel del ItemTemplate del FormView El ltimo paso es dar formato al valor que aparece en UnitsInStock en una fuente roja si el valor es menor o igual a 10. Para realizar esto es necesario acceder mediante programacin al control UnitsInStockLabel en el ItemTemplate y establezca sus propiedades de estilo de modo que su texto se muestre en rojo. Para acceder a un control web en una plantilla, utilice el mtodo FindControl (ControlID) de la siguiente forma:
Dim someName As WebControlType = _ CType([Link]("controlID"), WebControlType)
Para
nuestro
ejemplo
queremos
acceder
un
control
Label
cuyo
ID
es
Una vez que tengamos referenciado el control web mediante programacin, podemos modificar sus propiedades relacionadas con el estilo segn sea necesario. Al igual que con el ejemplo anterior, hemos creado una clase CSS [Link] llamado LowUnitsInStockEmphasis. Para aplicar este estilo al control web Label, establezca su correspondiente propiedad CssClass.
Protected Sub LowStockedProductsInRed_DataBound _ (sender As Object, e As [Link]) _ Handles [Link] Dim product As [Link] = _ CType(CType([Link], [Link]).Row, _ [Link]) If Not [Link]() AndAlso [Link] <= 10 Then Dim unitsInStock As Label = _ CType([Link]("UnitsInStockLabel"), Label) If unitsInStock IsNot Nothing Then [Link] = "LowUnitsInStockEmphasis" End If End If End Sub
Nota: La sintaxis para dar formato a una plantilla mediante programacin accede al control web usando FindControl (controlID) y luego establece sus propiedades relacionadas con el estilo; tambin podemos utilizarlo cuando utilizamos TemplateFields en el control DetailsView o GridView. Examinaremos los TemplateFields en el prximo tutorial. La figura 7 muestra un FormView cuando muestra un producto cuyo UnitsInStock es mayor a 10, mientras que el producto de la figura 8 tiene un valor inferior a 10.
Figura 7. Los productos con suficientes unidades en existencia, no se les aplica el formato personalizado
Figura 8. Las unidades en existencia aparecen rojo para aquellos productos cuyo valor es menor o igual a 10 Dar Formato con el evento RowDataBound del GridView Anteriormente hemos examinado la secuencia de pasos de los controles DetailsView y FormView durante el progreso de enlace de datos. Miraremos una vez estos pasos una vez, muy por encima para recordar. 1. El control de datos web genera el evento DataBinding. 2. Los datos son enlazados al control web 3. El control de datos web desencadena el evento DataBound. Estos tres sencillos pasos son suficientes para el DetailsView y el FormView, ya que muestran un nico registro. Para el control GridView que muestra todos los registros enlazados a l (no solo el primero), el paso 2 es mucho ms complicado.
En el paso 2, el GridView enumera la fuente de datos y para cada registro crea una instancia GridViewRow que se une al registro actual de la misma. Para cada GridViewRow agregado a los controles GridView, se producen dos eventos:
RowCreated, se genera luego que se crea el GridViewRow RowDataBound, se genera despus que el registro actual es enlazado al
GridViewRow
Luego para el control GridView, el enlace de datos se describe ms exactamente por la siguiente secuencia de pasos: 1. El GridView genera el evento DataBinding. 2. Los datos son enlazados al control GridView. Para cada registro del origen de datos: 1. Se crea un objeto GridViewRow 2. Se genera el evento RowCreated 3. Se enlaza el registro a la GridViewRow 4. Se genera el evento RowDataBound 5. El GridView se agrega a la coleccin Rows 3. El GridView desencadena el evento DataBound. Para personalizar el formato de los registros individuales del GridView, tenemos que crear un controlador de evento para el evento RowDataBound. Para ilustrar esto, vamos a aadir un GridView a la pgina [Link] que muestre el nombre, categora y precio de cada producto, destacando aquellos productos cuyo precio es inferior a $10,00 con un fondo de color amarillo. Paso 7: Visualizacin de informacin de los productos en un GridView Agregue un GridView por debajo del FormView del ejemplo anterior y establezca su propiedad ID en HighlightCheapProducts. Como ya tenemos un ObjectDataSource que devuelve todos los productos en la pgina, enlazaremos el GridView a l. Por ltimo modificamos los BoundFields del GridView para que incluya solamente los nombres de los productos, categoras y precios. Despus de estas ediciones el marcado del GridView ser similar a:
<asp:GridView ID="HighlightCheapProducts" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" EnableViewState="False" runat="server"> <Columns> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" /> </Columns> </asp:GridView>
La figura 9 muestra nuestro progreso hasta el momento cuando lo visualizamos a travs de un navegador.
Figura 9. El GridView muestra el nombre, categora y precio para cada producto Paso 8: Determinacin mediante programacin del valor de los datos en el controlador de eventos RowDataBound Cuando el ProductsDataTable es enlazado al GridView sus instancias ProductsRow se enumeran y para cada ProductRow se crea un GridViewRow. La propiedad DataItem del GridViewRow es asignada a un ProductsRow en particular, tras lo cual se produce el controlador de eventos RowDataBound del GridView. Para determinar el valor UnitPrice de cada producto enlazado al GridView es necesario crear un controlador de eventos
para el evento RowDataBound del GridView. Este controlador de eventos puede inspeccionar el valor UnitPrice para el actual GridViewRow y tomar una decisin del formato para esa fila. Este controlador de eventos se puede crear utilizando la misma serie de pasos que con los controles FormView y DetailsView.
Figura 10. Crear un controlador de eventos para el evento RowDataBound del GridView Crear el controlador de evento de esta forma hace que el siguiente cdigo sea agregado a la porcin de cdigo de la pgina [Link]:
Protected Sub HighlightCheapProducts_RowDataBound _ (sender As Object, e As [Link]) _ Handles [Link] End Sub
Luego de que se genera el evento RowDataBound, el controlador de eventos pasa como un segundo parmetro un objeto de tipo GridViewRowEventArgs, que tiene una propiedad denominada Row. Esta propiedad devuelve una referencia al GridViewRow que acaba de enlazar los datos. Para acceder a la instancia ProductsRow enlazada al GridViewRow usamos la propiedad DataItem de este modo:
Protected Sub HighlightCheapProducts_RowDataBound _ (sender As Object, e As [Link]) _ Handles [Link] Dim product As [Link] = _ CType(CType([Link],[Link])Row,[Link]) If Not [Link]() AndAlso [Link] < 10 Then End If End Sub
Cuando se trabaja con el controlador de eventos RowDataBound es importante tener en cuenta que el control GridView se compone de diferentes tipos de filas y que este evento se activa para todos los tipos de fila. Un tipo GridViewRow puede ser determinado por su propiedad RowType y puede tener uno de los posibles valores:
DataRow, una fila que esta enlazada a un registro del DataSource del GridView EmptyDataRow, la fila que aparece si el DataSource del GridView esta vacio Footer, el pie de pgina, aparece si la propiedad ShowFooter del GridView se
establece en True
Pager, Implementa la paginacin del GridView, las filas que muestra la interfaz
de paginacin
Como las filas EmptyDataRow, Footer, Header, y Pager no estn asociadas a un registro del DataSource, siempre tendrn un valor de Nothing para su propiedad DataItem. Por esta razn antes de intentar trabajar con la propiedad DataItem del GridViewRow actual, primero debe asegurarse de que estamos tratando con un DataRow. Este su puede lograr por el control de la propiedad RowType del GridViewRow del siguiente modo:
Protected Sub HighlightCheapProducts_RowDataBound _ (sender As Object, e As [Link]) _ Handles [Link] If [Link] =[Link] Then
Dim product As [Link] = _ CType(CType([Link], [Link]).Row, [Link]) If Not [Link]() AndAlso [Link] < 10 Then End If End If End Sub
Paso 9: Destacar la fila de color amarillo cuando el valor de UnitPrice sea menor de $10.00 El ltimo paso es resaltar mediante programacin el GridViewRow entero si el valor UnitPrice de esa fila es menor de $10.00. La sintaxis para acceder a las filas o celdas del GridView es lo mismo que con los DetailsView [Link] (index) para acceder a toda la fila, [Link] (index).Cells (index) para acceder a una celda en particular. Sin embargo cuando se genera el controlador de eventos RowDataBound, los datos enlazados al GridViewRow aun no se han agregado a la coleccin Rows del GridView. Por lo tanto, no podemos acceder a la instancia GridViewRow actual desde el controlador de eventos RowDataBound utilizando la coleccin Rows. En lugar de [Link] (index) puede hacer referencia a la actual instancia GridViewRow en el controlador de eventos RowDataBound utilizando [Link]. Es decir para resaltar la instancia GridViewRow actual desde el controlador de eventos RowDataBound se debe utilizar:
[Link] = [Link]
En lugar de establecer directamente la propiedad BackColor del GridViewRow, continuaremos con el uso de las clases CSS. Hemos creado una clase CSS llamada AffordablePriceEmphasis que establece el color del fondo en amarillo. El controlador de eventos RowDataBound completo es el siguiente:
Protected Sub HighlightCheapProducts_RowDataBound _ (sender As Object, e As [Link]) _ Handles [Link] If [Link] = [Link] Then Dim product As [Link] = _ CType(CType([Link],[Link]).Row, [Link]) If Not [Link]() AndAlso [Link] < 10 Then
Figura 11. Los productos ms asequibles son resaltados en amarillo Resumen En este tutorial vimos como dar formato a los controles GridView, FormView y DetailsView basados en los datos enlazados al control. Para realizar esto creamos un controlador de eventos para los eventos DataBound y RowDataBound, donde se examinaron los datos subyacentes, junto con un cambio de formato de ser necesario. Para acceder a los datos enlazados a un DetailsView o un FormView, se utiliza la propiedad DataItem en el controlador de eventos DataBound; para un GridView, cada propiedad DataItem de las instancias del GridViewRow contiene los datos enlazados a la fila, los cuales estn disponibles en el controlador de eventos RowDataBound. La sintaxis para ajustar el formato de los controles web mediante programacin depende del control web y como el formato de los datos son mostrados en la pantalla. Para los controles DetailsView y GridView, las filas y las celdas pueden ser accedidas
por un ndice ordinal. Para el FormView que utiliza las plantillas, el mtodo FindControl (controlID) se utiliza comnmente para localizar un control web dentro de la plantilla. En el siguiente tutorial veremos cmo utilizar las plantillas con los controles GridView y DetailsView. Adems veremos otra tcnica para personalizar el formato basado en los datos subyacentes.
12.
Para ofrecer mayor flexibilidad el control GridView ofrece el TemplateField, el cual se presenta usando una plantilla. Una plantilla puede incluir una combinacin de HTML esttico, controles web y sintaxis de enlace de datos. En este tutorial examinaremos como utilizar el TemplateField para lograr un mayor grado de personalizacin con el control GridView. Introduccin El GridView est compuesto de un conjunto de campos que indican que propiedades desde el origen de datos deben ser incluidas en la salida presentada junto con como los datos son mostrados. El tipo de campo ms sencillo es el BoundField, el cual muestra un valor de datos como texto. Otros tipos de campos muestran los datos usando elementos HTML alternativos. Por ejemplo el CheckBoxField se presenta como un CheckBox cuyo estado de verificacin depende del valor especificado por el campo de datos. El ImageField presenta una imagen cuyo origen de imagen est basado en un especfico campo de datos. Los HiperLink y Buttons cuyos estados dependen del valor de un campo subyacente, pueden ser presentados usando los tipos de campos HiperLinkField y ButtonField. Aunque los tipos de campo CheckBoxField, ImageField, HiperLinkField y ButtonField permiten una vista alternativa de los datos, se encuentran bastante limitados con respecto a su formato. Un CheckBoxField nicamente puede mostrar un solo CheckBoxField, al igual que un ImageField puede mostrar nicamente una sola imagen. Pero que sucede si un campo particular necesita mostrar algn texto, un CheckBox y una imagen, todos basados en diferentes valores de campos de datos? O que sucede si deseamos mostrar los datos usando un control Web diferente al CheckBox, Image, HiperLink o Button? Desafortunadamente, el BoundField limita su muestra a un solo campo de datos. Que sucede si deseamos mostrar dos o ms valores de campos de datos en una sola columna del GridView? Para acomodar este nivel de flexibilidad, el GridView ofrece el TemplateField que se representa usando una plantilla. Una plantilla puede incluir una mezcla de HTML esttico, controles Web y sintaxis de enlace a datos. Sin embargo, el TemplateField tiene una variedad de plantillas que pueden ser usadas para personalizar la presentacin de diferentes situaciones. Por ejemplo, el ItemTemplate es usado por
defecto para presentar las celdas de cada fila, pero el EditItemTemplate puede ser usado para personalizar la interfaz cuando editemos datos. En este tutorial examinaremos como usar un TemplateField para alcanzar un mayor grado de personalizacin con el control GridView. En el tutorial anterior vimos como personalizar el formato basado en los datos subyacentes, empleando los controladores de eventos DataBound y RowDataBound. Otra forma de personalizar el formato basados en los datos subyacentes es llamando mtodos de formateo dentro de una plantilla. Veremos esta tcnica en este tutorial. Para este tutorial usaremos los TemplateFields para personalizar la apariencia de una lista de empleados. Especficamente mostraremos todos los empleados, pero mostraremos los nombres y apellidos de los empleados en una sola columna, su fecha de contratacin en un control Calendar y una columna status que indica cuantos das ha sido empleado de la compaa.
Figura 1. Se utilizaran 3 TemplateFields para personalizar la visualizacin. Paso 1. Enlazar los datos al GridView En los escenarios de presentacin de informes es donde necesitamos usar TemplateFields para personalizar su apariencia. Para ms facilidad empezaremos creando un control GridView que inicialmente contendr solamente BoundFields y luego agregaremos nuevos TemplateFields o convertiremos los BoundFields existentes a
TemplateFields de ser necesario. Por lo tanto comenzaremos este tutorial agregando un GridView a la pgina por medio del Diseador y lo enlazaremos a un ObjectDataSource que devuelva una lista de todos los empleados. Estos pasos crearan un GridView con BoundFields para cada uno de los campos de los empleados. Abra la pgina [Link] y arrastre un GridView desde la caja de herramientas hasta el diseador. Desde la etiqueta inteligente del GridView, seleccione agregar un Nuevo ObjectDataSource que invoque el mtodo GetEmployees () de la clase EmployeesBLL.
Figura 2. Agregar un nuevo control ObjectDataSource que invoque el mtodo GetEmployees () Enlazando el GridView de esta forma, agregamos automticamente un BoundField para cada una de las propiedades de los empleados: EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo y Country. Para este informe no nos tomaremos la molestia de visualizar las propiedades EmployeID, ReportsTo o Country. Para remover estos BoundFields podemos: Utilizar el cuadro de dialogo campos, haga clic en el enlace Editar Columnas en la etiqueta inteligente del control GridView para que aparezca este cuadro de dialogo. Luego, seleccione los BoundFields de la lista inferior izquierda y haga clic en el botn rojo X para eliminar el BoundField.
Editar manualmente la sintaxis declarativa del GridView, desde la vista fuente, borre el elemento <asp: BoundField> del BoundField que desea eliminar.
Luego que elimine los BoundFields EmployeeID, ReportsTo y Country, el marcado de su GridView lucir como este:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="EmployeeID" DataSourceID="ObjectDataSource1"> <Columns> <asp:BoundField DataField="LastName" HeaderText="LastName" SortExpression="LastName" /> <asp:BoundField DataField="FirstName" HeaderText="FirstName" SortExpression="FirstName" /> <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" /> <asp:BoundField DataField="HireDate" HeaderText="HireDate" SortExpression="HireDate" /> </Columns> </asp:GridView>
Tmese un momento para ver nuestro progreso en un navegador. Hasta este momento deberamos ver una tabla con un registro por cada empleado y cuatro columnas: una para el apellido del empleado, una para su nombre, una para el cargo y una para su fecha de contratacin.
Figura 3. Se muestran los campos LastName, FirstName, Title y HireDate para cada empleado Paso 2. Mostrar el nombre y el apellido en una sola columna En la actualidad, los nombres y apellidos de cada empleado se muestran en columnas separadas. En su lugar puede resultar agradable combinarlos en una sola columna. Para realizar esto, tenemos que utilizar un TemplateField. Podemos tambin agregar un nuevo TemplateField, agregar el marcado y la sintaxis de enlace de datos necesarios y luego eliminar los BoundFields LastName y FirstName; o podemos convertir el BoundField FirstName en un TemplateField, editar el TemplateField para agregar el valor del LastName y luego eliminar el BoundField LastName. Ambos enfoques tienen el mismo resultado, pero personalmente prefiero convertir los BoundFields en TemplateField cuando sea posible, ya que la conversin agrega automticamente un Itemtemplate y un EditItemTemplate con controles web y la sintaxis de enlace a datos para imitar la apariencia y funcionalidad del BoundField. La ventaja es que tendremos que hacer menos trabajo con el TemplateField, ya que el proceso de conversin realiza parte del trabajo por nosotros. Para convertir un BoundField existente en un TemplateField, haga clic en el enlace editar columnas de la etiqueta inteligente del GridView, para que aparezca el cuadro de dialogo campos. Seleccione el BoundField a convertir de la lista inferior en la esquina izquierda y luego haga clic en el enlace Convertir este BoundField en un TemplateField en la esquina inferior derecha.
Figura 4. Convertir un BoundField en un TemplateField desde el cuadro de dilogo Campos Contine y convierta el BoundField FirstName en un TemplateField. Despus de este cambio no hay una diferencia perceptiva en el diseador. Esto se debe a que la conversin de un BoundField en un TemplateField mantiene la apariencia de un BoundField. A pesar que hasta el momento no existe ninguna diferencia visual en el diseador, este proceso de conversin ha remplazado la sintaxis declarativa del BoundField <asp:BoundField DataField="FirstName" HeaderText="FirstName" SortExpression="FirstName"/> con la siguiente sintaxis declarativa del TemplateField:
<asp:TemplateField HeaderText="FirstName" SortExpression="FirstName"> <EditItemTemplate> <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("FirstName") %>'></asp:TextBox> </EditItemTemplate> <ItemTemplate> <asp:Label ID="Label1" runat="server" Text='<%# Bind("FirstName") %>'></asp:Label> </ItemTemplate> </asp:TemplateField>
Como puede observar, el TemplateField consta de dos plantillas, un ItemTemplate que contiene un Label cuya propiedad Text se establece con el valor del campo de datos FirstName y un EditItemTemplate con un control TextBox cuya propiedad Text tambin est definida con el campo FirstName. La sintaxis de enlace de datos <%#Bind("fieldName") %> indica que el campo de datos FieldName esta enlazado a la propiedad especifica del control web. Para agregar el valor del campo de datos LastName a este TemplateField, necesitamos agregar otro control web Label en el ItemTemplate y enlazar su propiedad Text a LastName. Podemos realizar esto a travs del diseador o manualmente. Para realizar esto manualmente, simplemente agregue la siguiente sintaxis declarativa apropiada al ItemTemplate:
<asp:TemplateField HeaderText="FirstName" SortExpression="FirstName"> <EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("FirstName") %>'></asp:TextBox> </EditItemTemplate> <ItemTemplate> <asp:Label ID="Label1" runat="server" Text='<%# Bind("FirstName") %>'></asp:Label> <asp:Label ID="Label2" runat="server" Text='<%# Bind("LastName") %>'></asp:Label> </ItemTemplate> </asp:TemplateField>
Para agregar esto por medio del diseador, haga clic en el enlace editar Templates en la etiqueta inteligente del GridView. Esto mostrara la interfaz de edicin de plantillas del GridView. En la etiqueta inteligente de esta interfaz, hay una lista de los Templates del GridView. Como hasta el momento solo tenemos un TemplateField, el nico Template mostrado en la lista desplegable son los Templates para el TemplateField FirstName junto con el EmptyDataTemplate y PagerTemplate. Si se especifica el Template EmptyDataTemplate se utiliza para presentar la salida del GridView cuando no hay resultados en el enlace de datos del GridView; si se especifica el PagerTemplate se utiliza para presentar la interface de paginacin para un GridView que soporta paginacin.
Figura 5. Las Template del GridView pueden ser editadas por medio de un diseador Para mostrar tambin los TemplateField LastName y FirstName, arrastre un control Label desde la caja de herramientas al ItemTemplate del TemplateField FirstName en la interfaz de edicin de plantillas del GridView.
Figura 6. Agregar un control web Label al ItemTemplate del TemplateField del FisrtName Hasta el momento el control web Label agregado al TemplateField tiene su propiedad Text establecida en Label. Necesitamos cambiar esto para que en su lugar, esta propiedad sea enlazada al valor del campo de datos LastName. Para realizar esto, haga clic en la etiqueta inteligente del control Label y seleccione la opcin Editar enlace de datos.
Figura 7. Elija la opcin Editar Enlace de datos en la etiqueta inteligente del Label
Esto abrir el cuadro de dialogo DataBindings. Desde aqu puede seleccionar la propiedad que participa en el enlace de datos desde la lista de la izquierda, y seleccionar el campo para enlazar los datos desde la lista desplegable de la derecha. Seleccione la propiedad Text en la izquierda y el campo LastName de la derecha y haga clic en OK.
Figura 8. Enlace la propiedad Text al campo de datos LastName Nota: El cuadro de dialogo DataBindings le permite indicar si se debe realizar un enlace de datos de dos vas. Si se deja esta casilla sin marcar, se utiliza la sintaxis de enlace de datos <%#Eval (LastName)%> en lugar de usar <%#Bind(LastName)%>. Cualquiera de estos enfoques est bien para este tutorial. El enlace de datos bidireccional es muy importante cuando insertamos o modificamos datos. Sin embargo para simplemente mostrar datos, ambos enfoques trabajaran exactamente de la misma manera. Discutiremos el enlace de datos bidireccional en los futuros tutoriales. Tmese un momento para ver esta pgina a travs de un navegador. Como puede ver, El GridView aun incluye cuatro columnas, sin embargo la columna FirstName ahora muestra los valores de los campos de datos FirstName y LastName.
Figura 9. Los valores FirstName y LastName son mostrados en una sola columna Para completar este primer paso, quite el BoundField LastName y renombre la propiedad HeaderText del TemplateField FirstName a Nombre. Despus de estos cambios, el marcado declarativo del GridView debera tener el siguiente aspecto:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="EmployeeID" DataSourceID="ObjectDataSource1"> <Columns> <asp:TemplateField HeaderText="Name" SortExpression="FirstName"> <EditItemTemplate> <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("FirstName") %>'></asp:TextBox> </EditItemTemplate> <ItemTemplate> <asp:Label ID="Label1" runat="server" Text='<%# Bind("FirstName") %>'></asp:Label> <asp:Label ID="Label2" runat="server" Text='<%# Eval("LastName") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" /> <asp:BoundField DataField="HireDate" HeaderText= "HireDate" SortExpression="HireDate" /> </Columns>
</asp:GridView>
Figura 10. El LastName y el FirstName de cada empleado son mostrados en una sola columna Paso 3. Mostrar un control Calendar para mostrar la fecha de contratacin Mostrar un valor de un campo de datos como un texto en un GridView, es tan sencillo como usar un BoundField. Sin embargo, para ciertos escenarios los datos se pueden expresar mejor usando un control web en particular, en lugar de solo texto. Esta personalizacin de la visualizacin de datos es posible con TemplateFields. Por ejemplo, en lugar de mostrar la fecha de contratacin de los empleados como un texto, podramos mostrar un calendario (usando el control Calendar) con su fecha de contratacin resaltada. Para realizar esto comience convirtiendo el BoundField HireDate en un TemplateField. Sencillamente vaya a la etiqueta inteligente del GridView y haga clic en el enlace Editar Columnas, con lo cual aparece el cuadro de dialogo Campos. Seleccione el BoundField HiredDate y haga clic en Convertir este BoundField en un TemplateField.
Figura 11. Convierta el BoundField HiredDate en un TemplateField Como vimos en el paso 2, esto remplaza el BoundField con un TemplateField que contiene un ItemTemplate y un EditItemTemplate con un Label y un TextBox cuyas propiedades Text estn enlazadas al valor HiredDate usando la sintaxis de enlace de datos <%#Bind(HiredDate)%>. Para remplazar el texto con un control Calendar, editamos el Template eliminando el Label y agregando un control Calendar. Desde el diseador seleccione Editar Templates en la etiqueta inteligente del GridView y seleccione de la lista desplegable el Itemtemplate del TemplateField HiredDate. Luego borre el control Label y arrastre un control Calendar desde la caja de herramientas hasta la interfaz de edicin de plantillas.
Figura 12. Agregue un control Calendar al ItemTemplate del TemplateField del HiredDate Hasta el momento cada fila en el GridView contendr un control Calendar en su TemplateField HiredDate. Sin embargo el valor de la fecha de contratacin del empleado actual no est establecido en ninguno de los controles Calendar, ocasionando que cada control Calendar muestre por defecto la fecha y mes en curso. Para solucionar esto, tenemos que asignar las propiedades SelectedDate y Visible Date de los controles Calendar al HiredDate de cada empleado. Desde la etiqueta inteligente del control Calendar, seleccione Editar DataBindings. Luego enlace las propiedades SelectedDate y Visible Date al campo de datos HiredDate.
Figura 13. Enlace las propiedades SelectedDate y Visible Date al campo de datos HiredDate Nota: La fecha seleccionada del control Calendar no necesariamente ser visible. Por ejemplo un Calendar podra tener como fecha seleccionada Agosto 1, 1999, pero estar mostrando el mes y aos actuales. La fecha seleccionada y la fecha visible son especificadas por las propiedades SelectedDate y Visible Date del control Calendar. Como deseamos seleccionar la fecha de contratacin del empleado, asegrese que esta sea mostrada cuando enlazamos estas propiedades al campo de datos HiredDate. Cuando visualizamos la pgina en un navegador, el calendar ahora muestra el mes de la fecha de contratacin del empleado y seleccionara esa fecha en particular.
Figura 14. El HiredDate del empleado es mostrado en el control Calendar Nota: A diferencia de todos los ejemplos que hemos visto hasta ahora, para este tutorial no establecemos la propiedad EnableViewState=False para este GridView. La razn de esta decisin es porque dando clic a las fechas del control Calendar provocamos una devolucin de datos, estableciendo la fecha seleccionada del Calendar a la fecha que acabamos de dar clic. Sin embargo, si el ViewState del GridView esta deshabilitado, en cada devolucin, los datos del GridView son enlazados nuevamente a su origen de datos, lo que ocasiona que la fecha seleccionada en el Calendar se
establezca nuevamente en la fecha de contratacin del empleado, sobrescribiendo la fecha escogida por el usuario. Para este tutorial se trata de una discusin irrelevante ya que el usuario no debe actualizar la fecha de contratacin del empleado. Esta seria posiblemente la mejor configuracin para el control Calendar para que sus fechas no sean seleccionables. En cualquier caso, este tutorial muestra que en algunas circunstancias el ViewState debe ser deshabilitado con el fin de proporcionar cierta funcionalidad. Paso 4. Mostrar el nmero de das que el empleado ha trabajado con la compaa Hasta ahora hemos visto dos aplicaciones del TemplateFields: Combinar dos o ms valores de campos de datos en una columna, y Expresar el valor de un campo de datos usando un control web en lugar de texto
Un tercer uso del TemplateField est en la visualizacin de los metadatos relacionados a los datos subyacentes del GridView. Adems de mostrar las fechas de contratacin de los empleados, por ejemplo podramos mostrar cuantos das ha estado en su trabajo. Sin embargo, otro uso de los TemplateFields surge en los escenarios en que los datos subyacentes, deben ser mostrados de una forma diferente al formato con el que esta almacenado en la base de datos en una pgina web de informe. Imaginen que la tabla Empleados, tiene un campo Sexo que almacena los caracteres M o F indicando el sexo del empleado. Cuando mostramos esta informacin en una pgina web, podramos desear mostrar el sexo como Femenino y Masculino en lugar de M y F. Los dos escenarios pueden ser manejados con la creacin de un mtodo de formato en la clase del cdigo subyacente de la pgina [Link] (o en una biblioteca de clases separadas, implementada como un mtodo Share) que sea invocada desde el Template. Un mtodo de formato es invocado desde la Template usando la misma sintaxis de enlace de datos vista anteriormente. El mtodo de formato puede tomar cualquier nmero de parmetros, pero debe devolver un string. El string devuelto es el HTML que se ingresa en la plantilla. Para ilustrar este concepto, vamos a ampliar nuestro tutorial para mostrar una columna que muestre el total del nmero de das que cada empleado ha trabajado. El mtodo de
formato tomara un objeto [Link] y devuelve el nmero de das que un empleado ha trabajado como una cadena. Este mtodo puede ser agregado a la clase del cdigo subyacente de la pgina [Link], pero debe ser marcado como Protected o Public con el fin de que pueda accederse a l desde el Template.
Protected Function DisplayDaysOnJob(ByVal employee As [Link]) As String ' Make sure HiredDate is not NULL... if so, return "Unknown" If [Link]() Then Return "Unknown" Else ' Returns the number of days between the current ' date/time and HireDate Dim ts As TimeSpan = [Link]([Link]) Return [Link]("#,##0") End If End Function
Como el campo HiredDate puede contener valores de base de datos Nulls, primero debemos asegurarnos que el valor no es Null antes de proceder con el clculo. Si el valor HiredDate es Null, simplemente devolvemos la cadena Desconocido, si no es Null calculamos la diferencia entre la fecha actual y el valor de la fecha de contratacin y regresamos el nmero de das. Para utilizar este mtodo necesitamos invocarlo desde un TemplateField en el GridView utilizando la sintaxis de enlace de datos. Empiece agregando un nuevo TemplateField al GridView, haga clic en el enlace editar columnas en la etiqueta inteligente del control GridView y agregue un nuevo TemplateField.
Figura 15. Agregue un nuevo TemplateField en el GridView Establezca la propiedad Header de este nuevo TemplateField en Das en el trabajo y su propiedad HorizontalAlign del ItemStyle en center. Para llamar al mtodo DisplayDaysOnJob desde el Template, agregamos un ItemTemplate y usamos la siguiente sintaxis de enlace de datos:
<%# DisplayDaysOnJob(CType(CType([Link],[Link]).Row, [Link])) %>
[Link] devuelve un objeto DataRowView que corresponde al registro del DataSource enlazado al GridViewRow. Su propiedad el cual es Row pasado devuelve al el [Link] fuertemente tipiado, mtodo
DisplayDaysOnJob. Esta sintaxis de enlace de datos puede aparecer directamente en el ItemTemplate (como se muestra en la sintaxis declarativa de abajo) o puede ser asignada a la propiedad Text de un control web Label. Nota: De forma alternativa, en lugar de pasar una instancia EmployeesRow, podramos solamente pasar el valor de HiredDate usando <%#DisplayDaysOnJob (Eval(HireDate))%>. Sin embargo, el mtodo Eval devuelve un object, por lo cual tendremos que cambiar nuestra asignacin del mtodo DisplayDaysOnJob para que acepte en su lugar un parmetro de entrada de tipo object. Estamos a ciegas y no podemos llamar Eval(HiredDate) a un DateTime porque la columna HiredDate en la tabla Employees puede contener valores Nulls. Por lo tanto, necesitamos aceptar un object como un parmetro de entrada para el mtodo DisplayDaysOnJob, verificar para
ver si hay valores de base de datos Null (lo cual puede hacerse usando [Link] (objectToCheck)) y luego proceder acordemente. Debido a estas sutilezas, he optado por pasar toda la instancia EmployeesRow. En el siguiente tutorial veremos un ejemplo ms apropiado para usar la sintaxis Eval(ColumnName) pasando un parmetro de entrada en un mtodo de formato. A continuacin mostramos la sintaxis declarativa para nuestro GridView luego que ha sido agregado el TemplateField y el mtodo DisplayDaysOnJob es llamado desde el ItemTemplate:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="EmployeeID" DataSourceID="ObjectDataSource1"> <Columns> <asp:TemplateField Header Text="Name" SortExpression="FirstName"> <EditItemTemplate> <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("FirstName") %>'></asp:TextBox> </EditItemTemplate> <ItemTemplate> <asp:Label ID="Label1" runat="server" Text='<%# Bind("FirstName") %>'></asp:Label> <asp:Label ID="Label2" runat="server" Text='<%# Eval("LastName") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" /> <asp:TemplateField HeaderText="HireDate" SortExpression= "HireDate"> <EditItemTemplate> <asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("HireDate") %>'></asp:TextBox> </EditItemTemplate> <ItemTemplate> <asp:Calendar ID="Calendar1" runat="server" SelectedDate='<%# Bind("HireDate") %>' VisibleDate='<%# Eval("HireDate") %>'>
</asp:Calendar> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Days On The Job"> <ItemTemplate> <%# [Link]).Row, [Link])) %> </ItemTemplate> <ItemStyle HorizontalAlign="Center" /> </asp:TemplateField> </Columns> </asp:GridView> DisplayDaysOnJob(CType(CType([Link],
Figura 16. Se muestran el nmero de das que el empleado a trabajo en la empresa Resumen El TemplateField en el control GridView permite un alto grado de flexibilidad en la presentacin de los datos que est disponible con los otros controles de campo. Los TemplateFields son ideales para situaciones donde:
Mltiples campos de datos necesitan ser mostrados en una sola columna en el GridView Los datos son expresados mejor usando un control web en lugar de un texto plano Las salidas dependen de los datos subyacentes, como al mostrar los metadatos o en reformatear los datos.
Adicionalmente para personalizar la visualizacin de datos, los TemplateFields tambin son usados para personalizar las interfaces de usuario usadas para editar o insertar datos, como veremos en futuros tutoriales. Los siguientes dos tutoriales continan explorando los Templates, partiendo de una mirada del uso de TemplateFields en un DetailsView. Luego pasaremos al FormView que utiliza plantillas en lugar de campos para proporcionar una mayor flexibilidad en el diseo y estructura de los datos.
13.
Las mismas capacidades de TemplateFields disponibles con el GridView tambin estn disponibles en el control DetailsView. En este tutorial vamos a mostrar un producto a la vez usando un DetailsView que contiene TemplateFields. Introduccin Los TemplateField ofrecen un mayor grado de flexibilidad en la reproduccin de los datos que los BoundField, CheckBoxField, HiperLinkField y los otros controles de campos de datos. En el tutorial anterior vimos como usar un TemplateField en un GridView para: Mostrar varios valores de campos de datos en una columna. Especficamente combinamos los campos FirstName y LastName en una sola columna del GridView. Usar un control web alternativo para mostrar el valor de un campo de datos. Vimos como mostrar el valor de la fecha de contratacin usando un control Calendar. Mostrar el estado de la informacin basados en los datos subyacentes. Aunque la tabla Employees no contiene una columna que devuelva los das que un empleado ha estado en el trabajo, fuimos capaces de mostrar esta informacin en el GridView del ejemplo del tutorial anterior usando un TemplateField y un mtodo de formato. Las mismas capacidades disponibles en los TemplateFields validas en un GridView tambin estn disponibles con el control DetailsView. En este tutorial mostraremos un producto a la vez usando un DetailsView que contiene dos TemplateFields. El primer TemplateField combinara los campos de datos UnitPrice, UnitsInStock y UnitsOnOrder en una sola fila del DetailsView. El segundo TemplateField mostrara el valor del campo Discontinued, el cual usara un mtodo de formato para mostrar Si, si el Discontinued es True y No si Discontinued es False.
Figura 1. Se utilizaran dos TemplateFields para personalizar la visualizacin Paso 1. Enlazar los datos al DetailsView: Como discutimos en el tutorial anterior, cuando trabajamos con TemplateFields a menudo es fcil comenzar creando un control DetailsView que contiene solamente BoundFields y luego agregar TemplateFields o convertir los BoundFields existentes a TemplateFields si es necesario. Por lo tanto comenzamos este tutorial agregando un DetailsView a la pgina por medio del diseador y enlazarlo a un ObjectDataSource que devuelva la lista de los productos. Estos pasos crearan un DetailsView con BoundFields para cada uno de los valores de campo no booleanos de los productos y un CheckBoxField para cada uno de los valores de campos booleanos (Discontinued). Abra la pgina [Link] y arrastre un DetailsView desde la caja de herramientas hasta el diseador. Desde la etiqueta inteligente del DetailsView seleccione agregar un nuevo ObjectDataSource que invoque el mtodo GetProducts () de la clase ProductsBLL.
Figura 2. Agregue un nuevo control ObjectDataSource que invoque el mtodo GetProducts () Para este informe removemos los BoundFields ProductID, SupplierID, CategoryID y ReorderLevel. Luego reordene los BoundFields para que los BoundFields CategoryName y SupplierName aparezca inmediatamente despus del BoundField ProductName. Sintase libre de ajustar las propiedades HeaderText y dar formato a las propiedades para que los BoundFields aparezcan mejor. Como con el GridView, estas ediciones a nivel de BoundField pueden realizarse por medio del cuadro de dialogo campos (accedemos dando clic en el enlace editar campos en la etiqueta inteligente del DetailsView) o por medio de la sintaxis declarativa. Por ltimo limpie los valores de las propiedades Heigth y Width del DetailsView con el fin de permitir que el DetailsView se expanda basado en los datos mostrados y habilite la casilla de verificacin Habilitar Paginacin en la etiqueta inteligente. Despus de estos cambios, el marcado declarativo de su DetailsView lucir similar al siguiente:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True" EnableViewState="False"> <Fields> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True" SortExpression="SupplierName" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" /> <asp:BoundField DataField="UnitPrice" HeaderText="Price" SortExpression="UnitPrice" /> <asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock" SortExpression="UnitsInStock" /> <asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order" SortExpression="UnitsOnOrder" /> <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued" /> </Fields> </asp:DetailsView>
Tmese un momento para ver la pgina a travs de un navegador. Hasta el momento veremos un solo producto (Chai) con filas que muestran el nombre del producto, categora, proveedor, precio, unidades en existencia, unidades ordenadas y su estado de descontinuado.
Figura 3. Los detalles del producto son mostrados usando una serie de BoundFields
Paso 2. Combinar el precio, unidades ordenadas y unidades de existencia en una sola fila El DetailsView tiene una fila para los campos UnitPrice, UnitsInStock y UnitsOnOrder. Podemos combinar estos campos de datos en una sola fila con un TemplateField, ya sea agregando un nuevo TemplateField o convirtiendo uno de los BoundFields UnitPrice, UnitsInStock y UnitsOnOrder existentes en un TemplateField. Aunque personalmente prefiero convertir un BoundField existente en un TemplateField, practicaremos agregando un Nuevo TemplateField. Comencemos haciendo clic en el enlace Editar campos en la etiqueta inteligente del DetailsView para que aparezca el cuadro de dialogo campos. Luego agregue un nuevo TemplateField y establezca su propiedad HeaderText en Precio e Inventario y mueva el nuevo TemplateField para que se posicione encima del BoundField UnitPrice.
Figura 4. Agregue un nuevo TemplateField al control DetailsView Como este nuevo TemplateField contendr los valores actualmente mostrados en los BoundFields UnitPrice, UnitsInStock y UnitsOnOrder, vamos removerlos. La ltima tarea de este paso es la de definir el marcado del ItemTemplate para el TemplateField Precio e Inventario, que se puede realizar por medio de la interfaz de edicin de Template del DetailsView en el diseador o manualmente por medio de la sintaxis declarativa del control. Al igual que con el GridView, se puede acceder a la interface de edicin de las plantillas del DetailsView dando clic en el enlace editar Templates en la etiqueta inteligente. Desde aqu podemos seleccionar el Template a
editar desde la lista desplegable y luego agregar cualquier control web desde la caja de herramientas. Para este tutorial comenzamos agregando un control Label al ItemTemplate del TemplateField Precio e Inventario. Luego haga clic en el enlace editar DataBindings en la etiqueta inteligente del control Label y enlace la propiedad Text al campo UnitPrice.
Figura 5. Enlace la propiedad Text del Label al campo de datos UnitPrice Dar formato al precio como una moneda Con esta edicin, el control web Label del TemplateField Precio e Inventario muestra solamente el precio del producto seleccionado. La figura 6 muestra una captura de pantalla de nuestro progreso hasta el momento, vista a travs de un navegador.
Tenga en cuenta que el precio del producto no est formateado como moneda. Con un BoundField dar formato es posible estableciendo su propiedad HtmlEncode a False y la propiedad DataFormatString en {0:formatSpecifier}. Sin embargo para un TemplateField cualquier instruccin de formato debe ser especificado en la sintaxis de enlace de datos o por medio de un mtodo de formato definido en algn lugar dentro del cdigo de la aplicacin (como en la clase del cdigo subyacente de la pgina [Link]). Para especificar le formato de la sintaxis de enlace de datos usada en el control Label, regresamos al cuadro de dialogo DataBindings dando clic en el enlace editar DataBindings en la etiqueta inteligente del Label. Usted puede escribir las instrucciones de formato directamente en la lista desplegable formato, o seleccionar una de las cadenas de formato definidas. Al igual que con la propiedad DataFormatString de los BoundFields, el formato es especificado usando {0:formatSpecifier}. Para el campo UnitPrice usamos el formato moneda especificado seleccionando el valor apropiado de la lista desplegable o escribiendo manualmente {0:c}.
Figura 7. Formatear el precio como moneda Mediante declaracin, la especificacin del formato es indicado como un segundo parmetro en los mtodos Bind o Eval. Las modificaciones que acabamos de hacer por medio del Diseador originan la siguiente expresin de enlace de datos en el marcado declarativo:
Agregar los campos de datos restantes al TemplateField Hasta este momento hemos mostrado y formateado el campo de datos UnitPrice en el TemplateField Precio e Inventario, pero aun necesitamos mostrar los campos UnitsInStock y UnitsOnOrder. Mostraremos estos en una linea debajo del precio y en parntesis. Desde la interface de edicin de Templates en el diseador, el marcado puede ser agregado posicionando su cursor dentro del Template y escribiendo el texto que ser mostrado. Por otra parte este marcado puede ser agregado directamente en la sintaxis declarativa. Agregue el marcado esttico, los controles web Label y la sintaxis de enlace de datos para que el TemplateField Precio e Inventario muestre la informacin del precio e inventario de la siguiente manera:
UnitPrice
(In Stock / On Order: UnitsInStock / UnitsOnOrder)
Despus de realizar esta tarea, el marcado declarativo del DetailsView lucir similar al siguiente:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True" EnableViewState="False"> <Fields> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True" SortExpression="SupplierName" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" /> <asp:TemplateField HeaderText="Price and Inventory"> <ItemTemplate> <asp:Label ID="Label1" runat="server" Text='<%# Eval("UnitPrice", "{0:C}") %>'></asp:Label>
<br /> <strong> (In Stock / On Order: </strong> <asp:Label ID="Label2" runat="server" Text='<%# Eval("UnitsInStock") %>'></asp:Label> <strong>/</strong> <asp:Label ID="Label3" runat="server" Text='<%# Eval("UnitsOnOrder") %>'> </asp:Label><strong>)</strong> </ItemTemplate> </asp:TemplateField> <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued" /> </Fields> </asp:DetailsView>
Con estos cambios, consolidamos la informacin de precio e inventario en un solo DetailsView row
Figura 8. La informacin del Precio e Inventario es mostrada en una sola fila Paso 3. Personalizar la informacin del campo Discontinued La columna Discontinued de la tabla Products es un valor bit que indica si el producto ha sido descontinuado. Cuando enlazamos un DetailsView (o GridView) a un control de origen de datos, los valores de campos booleanos como Discontinued son implementados como CheckBoxFields, mientras que los valores de campos no booleanos como ProductID, ProductName, entre otros son implementados como
BoundFields. Los CheckBoxFields se presentan como una casilla de verificacin deshabilitada, que es habilitada si el valor del campo de datos es True y deshabilitada en caso contrario. En lugar de mostrar el CheckBoxField podramos desear mostrar en su lugar un texto que indique si el producto est o no descontinuado. Para realizar esto podemos remover el CheckBoxField del DetailsView y luego agregar un BoundField cuya propiedad DataField sea establecida en Discontinued. Tmese un momento para hacer esto. Luego de estos cambios el DetailsView muestra el texto True para los productos descontinuados y False para los productos que aun estn activos.
Figura 9. Se utilizan las cadenas True y False para mostrar el estado de Discontinued Imagine que en lugar de usar las cadenas True y False deseamos usar Si y No. Estas personalizaciones pueden realizarse con la ayuda de un TemplateField y un mtodo de formato. Un mtodo de formato puede tomar cualquier numero de parmetros de entrada, pero debe devolver el HTML (como una cadena) para ingresar en el Template. Agregue un mtodo de formato a la clase de cdigo subyacente de la pgina [Link] llamado DisplayDiscontinuedAsYESorNO que acepte un objeto [Link] como un parmetro de entrada y devuelva una cadena. Como discutimos en el tutorial anterior, este mtodo debe ser marcado como Public o Protected con el fin que pueda ser accedido desde el Template.
Protected Function DisplayDiscontinuedAsYESorNO(discontinued As Boolean) As String If discontinued Then Return "YES" Else Return "NO" End If End Function
Este mtodo examina el parmetro de entrada (discontinued) y devuelve SI si es True, NO en caso contrario. Nota: En el mtodo de formato examinado en el tutorial anterior aclaramos que el campo de datos pasado podra contener valores Nulls y por lo tanto era necesario verificar si la propiedad HiredDate de los empleados tiene un valor de base de datos Null antes de acceder a la propiedad HiredDate de EmployeesRow. Aqu no es necesaria una verificacin ya que la columna Discontinued nunca podr tener valores de base de datos Nulls asignados. Por otra parte esta es la razn por la cual el mtodo puede aceptar un parmetro de entrada booleano en lugar de tener que aceptar una instancia ProductsRow o un parmetro de tipo object. Con este mtodo de formato completo, lo restante es llamarlo desde el ItemTemplate del TemplateField. Para crear el TemplateField, remueva el BoundField Discontinued y agregue un nuevo TemplateField o convierta el BoundField Discontinued en un TemplateField. Luego desde la vista del marcado declarativo, edite el TemplateField para que contenga solamente un ItemTemplate que invoque el mtodo DsiplayDiscontinuedYESorNO, pasando el valor actual de la propiedad Discontinued de la instancia ProductsRow actual. Este puede ser accedido a travs del mtodo Eval. Especficamente el marcado declarativo del TemplateField lucir as:
<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued"> <ItemTemplate> <%# DisplayDiscontinuedAsYESorNO([Link](Eval("Discontinued")) %> </ItemTemplate> </asp:TemplateField>
Esto har que el mtodo DisplayDiscontinuedAsYESorNO sea invocado cuando presentamos el DetailsView, pasando el valor Discontinued de la instancia ProductRow.
Como el mtodo Eval devuelve un valor de un tipo object, pero el mtodo DisplayDiscontinuedAsYESorNO espera un parmetro de entrada de tipo Boolean, nosotros moldeamos los mtodos Eval para devolver un valor Boolean. Entonces el mtodo DisplayDiscontinuedAsYESorNO devolver SI o NO dependiendo del valor que recibe. El valor devuelto es el que se muestra en esta fila del DetailsView (Ver Figura 10).
Figura 10. Ahora se muestran los valores YES o NO en la fila Discontinued Resumen Los TemplateField en el control DetailsView permiten un alto grado de flexibilidad en la visualizacin de datos que est disponible con los otros controles de campos y son ideales para situaciones donde: Es necesario mostrar mltiples campos de datos en una sola columna del GridView. Es mejor expresar los datos usando un control web en lugar de un texto sin formato. La salida depende de los datos subyacentes, tal como la presentacin de los metadatos o en reformatear los datos. Mientras los TemplateFields permiten un mayor grado de flexibilidad en la presentacin de los datos subyacentes del DetailsView, las salidas del DetailsView todava se siente un poco cuadriculada, ya que cada fila se presenta como una fila en un archivo HTML <table>.
El control FormView ofrece un mayor grado de flexibilidad en la configuracin de la presentacin de sus salidas. El FormView no contiene campos en su lugar tiene una serie de plantillas (ItemTemplate, EditItemTemplate, HeaderTemplate y as sucesivamente). En nuestro prximo tutorial veremos cmo utilizar el FormView para lograr un mayor control en la capa de presentacin.
14.
A diferencia del DetailsView, el FormView no se compone de campos. En cambio el FormView se representa mediante el uso de plantillas. En este tutorial examinaremos el control FormView para presentar un diseo menos rgido de los datos. Introduccin En los ltimos dos tutoriales vimos como personalizar las salidas de los controles GridView y DetailsView usando TemplateFields. Los Templatefields permiten personalizar altamente los contenidos para un campo especfico, pero tanto para los controles GridView y DetailsView tienen una apariencia de aspecto cuadriculado. Para muchos escenarios un diseo cuadriculado es lo ideal, pero a veces se necesita una pantalla ms fluida y menos rgida. Cuando se muestra un registro nico con un diseo fluido podemos utilizar un control FormView. A diferencia del DetailsView. El FormView no est compuesto de campos. Usted no puede agregar un BoundField o un TemplateField al FormView. En su lugar el FormView es presentado usando plantillas. Piense en el FormView como un control DetailsView que contiene un solo TemplateField. El FormView soporta las siguientes plantillas: ItemTemplate usado para presentar un registro especifico mostrado en el FormView HeaderTemplate usado para especificar una fila de encabezado opcional FooterTemplate usado para especificar opcionalmente una fila de pie de pagina EmptyDataTemplate cuando el DataSource del FormView carece de registros, el EmptyDataTemplate se utiliza en lugar del ItemTemplate para presentar el marcado del control. PagerTemplate se puede utilizar para personalizar la interfaz de paginacin del FormView que permite la paginacin. EditItemTemplate/InsertItemTemplate utilizada para personalizar la interfaz de edicin o la interfaz de insercin de los FormView que soportan esta funcionalidad. En este tutorial examinaremos como usar el control FormView para presentar una pantalla de productos menos rgida. En lugar de tener campos para el nombre, categora, proveedor y as sucesivamente. El ItemTemplate del FormView muestra estos
valores usando una combinacin de un elemento de encabezado y una <table> (Ver Figura 1).
Figura 1. El FormView rompe con el diseo de cuadriculado visto en el DetailsView Paso 1: Enlazar los datos al FormView Abra la pgina [Link] y arrastre un FormView desde la barra de herramientas hasta el diseador. Cuando agregamos el FormView este aparece como una caja gris, indicndonos que es necesario un ItemTemplate.
Figura 2. El FormView no puede presentarse hasta que se proporcione el ItemTemplate El ItemTemplate puede ser creado manualmente (a travs de la sintaxis declarativa) o puede ser creado automticamente enlazando el FormView a un control de origen de datos por medio del diseador. El ItemTemplate creado automticamente contiene
HTML que muestra los nombres de cada campo y un control Label cuyas propiedades Text son enlazadas a los valores de los campos. Este enfoque tambin crea automticamente un InsertTemplate y EditTemplate, los cuales son poblados con controles de entrada para cada uno de los campos de datos devueltos por los controles de origen de datos. Si usted desea crear automticamente la plantilla, desde la etiqueta inteligente del FormView agregue un nuevo control ObjectDataSource que invoque el mtodo GetProducts () de la clase ProductsBLL. Esto creara un FormView con un ItemTemplate, InsertItemTemplate y un EditTemplate. Desde la vista Cdigo, remueva el ItemTemplate y el EditItemTemplate ya que no estamos interesados aun en crear un FormView que soporte la edicin o la insercin. Luego limpiemos el marcado del ItemTemplate para que podamos tener un borrn y cuenta nueva para trabajar. Si prefiere construir el ItemTemplate manualmente, podemos agregar y configurar el ObjectDataSource arrastrndolo desde el cuadro de herramientas al diseador. Sin embargo no establezca el origen de datos desde el diseador. En su lugar vaya a la vista cdigo fuente y configure manualmente la propiedad DataSourceID del FormView al valor ID del ObjectDataSource. Luego agregue manualmente el ItemTemplate. Independientemente del enfoque que desee utilizar, hasta este punto el marcado declarativo del FormView debe ser similar a:
<asp:FormView ID="FormView1" runat="server" DataSourceID="ObjectDataSource1"> <ItemTemplate> </ItemTemplate> </asp:FormView>
Tome un momento para seleccionar la casilla de verificacin Habilitar Paginacin en la etiqueta inteligente del FormView, lo cual agregara el atributo AllowPaging=True a la sintaxis declarativa del FormView. As mismo defina la propiedad EnableViewState en Falso. Paso 2: Defina el marcado del ItemTemplate Con el FormView enlazado al control ObjectDataSource y configurado para que admita la paginacin, estamos listos para especificar el contenido del ItemTemplate. Para este
tutorial vamos a tener el nombre del producto mostrado en un encabezado <h3>. Luego usaremos un HTML <table> para mostrar las propiedades de los productos restantes en una tabla de cuatro columnas, en la primera y tercer columna mostramos los nombres de la propiedad y en la segunda y cuarta mostramos sus valores. Este marcado se puede introducir a travs de la interfaz de edicin de plantillas del FormView en el diseador o introducirlo manualmente a travs de la sintaxis declarativa. Cuando se trabaja con plantillas normalmente resulta ms rpido trabajar directamente con la sintaxis declarativa, pero no dude en utilizar la tcnica con la que se sienta ms cmodo. El siguiente marcado muestra el marcado declarativo del FormView luego que la estructura ItemTemplate se ha completado:
<asp:FormView ID="FormView1" runat="server" DataSourceID="ObjectDataSource1" AllowPaging="True" EnableViewState="False"> <ItemTemplate> <hr /> <h3><%# Eval("ProductName") %></h3> <table border="0"> <tr> <td class="ProductPropertyLabel">Category:</td> <td class="ProductPropertyValue"> <%# Eval("CategoryName") %></td> <td class="ProductPropertyLabel">Supplier:</td> <td class="ProductPropertyValue"> <%# Eval("SupplierName")%></td> </tr> <tr> <td class="ProductPropertyLabel">Price:</td> <td class="ProductPropertyValue"><%# Eval("UnitPrice", "{0:C}") %></td> <td class="ProductPropertyLabel">Units In Stock:</td> <td class="ProductPropertyValue"> <%# Eval("UnitsInStock")%></td> </tr> <tr> <td class="ProductPropertyLabel">Units On Order:</td> <td class="ProductPropertyValue">
<%# Eval("UnitsOnOrder") %></td> <td class="ProductPropertyLabel">Reorder Level:</td> <td class="ProductPropertyValue"> <%# Eval("ReorderLevel")%></td> </tr> <tr> <td class="ProductPropertyLabel">Qty/Unit</td> <td class="ProductPropertyValue"> <%# Eval("QuantityPerUnit") %></td> <td class="ProductPropertyLabel">Discontinued:</td> <td class="ProductPropertyValue"> <asp:CheckBox runat="server" Enabled="false" Checked='<%# Eval("Discontinued") %>' /> </td> </tr> </table> <hr /> </ItemTemplate> </asp:FormView>
Por ejemplo observe que la sintaxis de enlace de datos - <%# Eval(ProductName)%>, puede ser ingresada directamente en la salida de la plantilla. Es decir no es necesario asignar la propiedad Text a un control Label. Por ejemplo nosotros tenemos el valor ProductName <h3>Chai</h3i>. Las clases CSS ProductPropertyLabel y ProductPropertyValue se utilizan para especificar el estilo de las propiedades del nombre y valor de los productos en la <table>. Estas clases CSS se definen en [Link] y ocasionan que las propiedades nombres sean en negrita y alineados a la derecha y agrega un relleno a la derecha de la propiedad valores. Dado que no existen CheckBoxFields disponibles en el FormView, para mostrar una casilla de verificacin, es necesario que agreguemos nuestro propio control CheckBox. La propiedad Enable se establece en False, lo que lo convierte en solo lectura y la propiedad Checked del CheckBox esta enlazada al valor del campo de datos Discontinued. mostrado en un elemento <h3> utilizando <h3><%#Eval(ProductName)%></h3>, que para el producto Chai ser algo como
Con el ItemTemplate completo, la informacin del producto se muestra de una forma mucho ms fluida. Compare la salida del DetailsView de los ltimos tutoriales (Figura 3) con el resultado generado por el FormView de este tutorial (Figura 4).
Figura 4. Salida Fluida del DetailsView Resumen Aunque los controles GridView y DetailsView pueden personalizar sus salidas usando TemplateFields, ambos presentan sus datos en una forma cuadrada. Para ocasiones
cuando es necesario mostrar un solo registro usando un diseo menos rgido, el FormView es una opcin ideal. Al igual que el DetailsView, el FormView muestra un nico registro de su DataSource, pero a diferencia del DetailsView este solo se compone de plantillas y no es compatible con los campos. Como vimos en este tutorial, el FormView presenta un diseo ms flexible al mostrar un nico registro. En futuros tutoriales examinaremos los controles DataList y Repeater, que proporcionan el mismo nivel de flexibilidad que los FormViews, pero son capaces de mostrara varios registros (como el GridView).
15.
GRIDVIEW
El resumen de la informacin a menudo es mostrado en la parte inferior del informe en una fila de resumen. El control GridView puede incluir una fila de pie de pgina, en cuyas celdas se puede ingresar mediante programacin los datos agregados. En este tutorial veremos cmo mostrar los datos agregados en esta fila de pie de pgina. Introduccin Adems de ver cada uno de los precios de los productos, unidades de existencia, unidades pedidas y niveles de pedidos, el usuario podra estar interesado en una informacin adicional, como un promedio del precio, el nmero total de unidades en existencia y as sucesivamente. Por lo tanto el resumen de la informacin es mostrado a menudo en una fila de resumen en la parte inferior del informe. El control GridView puede incluir una fila de pie de pgina en cuyas celdas se puede ingresar mediante programacin los datos agregados. Esta tarea nos plantea tres desafos: Configurar el GridView para que muestre la fila de pie de pagina Determinar el resumen de los datos, es decir cmo se calcula el precio promedio, el total de las unidades en existencia? Ingresar el resumen de los datos en las celdas apropiadas de la fila de pie de pgina. En este tutorial veremos cmo superar estos desafos. Especficamente crearemos una pgina que muestre todas las categoras en una lista desplegable, con los productos de la categora seleccionada mostrados en un control GridView. El GridView incluir una fila de pie de pgina que muestra el precio promedio y el nmero total de unidades en existencia y ordenadas para los productos de esa categora.
Figura 1. El resumen de la informacin se muestra en el pie de pgina del GridView Este tutorial con su interface Maestro/Detalle Categoras y Productos, construida bajo los conceptos tratados en el tutorial anterior Filtrado Maestro/Detalle con un DropDownList. Si todava no ha trabajado con el tutorial anterior, por favor revselo antes de continuar con este. Paso 1. Agregar el DropDownList Categories y el GridView Products Antes de preocuparnos por agregar la informacin resumida en el pie de pgina del GridView, primero construiremos el informe Maestro/Detalle. Una vez que hayamos completado este primer paso, veremos la forma de incluir el resumen de los datos. Comience abriendo la pgina [Link] de la carpeta
CustomFormatting. Agregue un control DropDownList y establezca su ID en Categories. Luego haga clic en el enlace Elegir Origen de datos en la etiqueta inteligente del DropDownList y seleccione la opcin agregar un nuevo ObjectDataSource llamado CategoriesDataSource CategoriesBLL. que invoque el mtodo GetCategories() de la clase
Figura 3. Hacer que el ObjectDataSource invoque el mtodo GetCategories() de la clase CategoriesBLL Despus de configurar el ObjectDataSource, el asistente nos devuelve al asistente de configuracin del origen de datos del DropDownList, en el cual necesitamos especificar cul es el valor del campo de datos que debemos mostrar y cual corresponde al valor del LisItems del DropDownList. Haga de CategoryName el campo a mostrar y utilice el CategoryID como valor.
Figura 4. Utilice los campos CategoryName y CategoryID como el Text y el Value del ListItem respectivamente Hasta el momento tenemos un DropDownList (Categories) que muestre las categoras en el sistema. Ahora necesitamos agregar un GridView que muestre los productos que pertenecen a la categora seleccionada. Sin embargo antes de hacerlo nos tomaremos un momento para revisar la casilla de verificacin Habilitar AutoPostBack en la etiqueta inteligente del DropDownList. Como se discuti en el tutorial Filtrado Maestro/Detalle con un DropDownList establezca la propiedad AutoPostBack del DropDownList en True, la pgina se actualizara cada vez que se cambie el valor del DropDownList. Esto hace que el GridView se actualice, mostrando los productos de la categora que acaba de seleccionar. Si la propiedad AutoPostBack est establecida en False (por defecto), cambiar la categora no origina ninguna devolucin de datos y por lo tanto no se actualizaran los productos de la fila.
Figura 5. Marque la opcin Habilitar AutoPostBack en la etiqueta inteligente del DropDownList Agregue un control GridView a la pgina con el fin de mostrar los productos para la categora seleccionada. Ajuste el ID del GridView a ProductsInCategory y enlacelo a un nuevo ObjectDataSource llamado ProductsInCategoryDataSource.
Figura 6. Agregar un nuevo ObjectDataSource llamado ProductsInCategoryDataSource Configure el ObjectDataSource para que invoque el mtodo
Figura
7.
Haga
que
el
ObjectDataSource
invoque
el
mtodo
GetProductsByCategoryID(categoryID) Como el mtodo GetProductsByCategoryID(categoryID) toma un parmetro de entrada, el paso final del asistente nos pide especificar el origen del valor del parmetro. Con el fin de mostrar los productos de la categora seleccionada, tomamos el parmetro desde el DropDownList Categories.
Despus de completar el asistente el GridView tendr un BoundField para cada una de las propiedades del producto. Limpiaremos los BoundField de modo que solo se muestren los BoundFields ProductName, UnitPrice, UnitsInStock y UnitsOnOrder. Sintase libre de agregar cualquier configuracin a nivel de campo a los BoundFields restantes (por ejemplo, formatear el UnitPrice como moneda). Despus de realizar estos cambios el marcado declarativo debe ser similar al siguiente:
<asp:GridView ID="ProductsInCategory" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ProductsInCategoryDataSource" EnableViewState="False"> <Columns> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice"> <ItemStyle HorizontalAlign="Right" /> </asp:BoundField> <asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock" SortExpression="UnitsInStock"> <ItemStyle HorizontalAlign="Right" /> </asp:BoundField> <asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order" SortExpression="UnitsOnOrder"> <ItemStyle HorizontalAlign="Right" /> </asp:BoundField> </Columns> </asp:GridView>
Hasta el momento tenemos un Informe Maestro/Detalle funcionando plenamente que muestra el nombre, precio unitario, unidades en existencia y unidades ordenadas para los productos que pertenecen a la categora seleccionada.
Figura 9. Obtener el valor del parmetro categoryID de la categora seleccionada en el DropDownList Paso 2. Visualizacion de un pie de pgina en el GridView En GridView puede mostrar una fila de encabezado y un pie de pgina. Estas filas se muestran dependiendo de los valores de sus propiedades ShowHeader y ShowFooter, respectivamente con ShowFooter por defecto en True y ShowFooter en False. Para incluir un pie de pgina en el GridView establezca su propiedad ShowFooter en True.
La fila de pie de pgina tiene una celda para cada uno de los campos definidos en el GridView, sin embargo por defecto estas celdas estn vacas. Tmese un momento para ver nuestro progreso en un navegador. Con la propiedad ShowFooter establecida en True, el GridView ahora incluye una fila de pie de pgina vaca.
Figura 11. El GridView ahora incluye una fila de pie de pgina La fila de pie de pgina en la figura 11 no se destaca, ya que tiene un fondo blanco. Crearemos una clase CSS FooterStyle en [Link] que especifique un color de fondo rojo oscuro y luego configure el archivo skin [Link] en el tema DataWebControls para que asigne esta clase CSS a la propiedad CssClass del FooterStyle del GridView. Si necesita recordar sobre skins y temas refirase de nuevo al tutorial Visualizacion de datos con el ObjectDataSource Comencemos agregando la siguiente clase CSS a [Link]:
.FooterStyle { background-color: #a33; color: White; text-align: right; }
La clase Css FooterStyle es similar en estilo a la clase HeaderStyle, aunque el color de fondo del HeaderStyle es ms oscuro y su texto est en negrita. Adems el texto del pie de pgina est alineado a la derecha mientras que el del encabezado est centrado. Luego asocie esta clase CSS con el pie de pgina del GridView, abra el archivo [Link] en el tema DataWebControls y establezca la propiedad Css del FooterStyle. Despus de esta adicin el marcado del archivo debe ser similar a:
<asp:GridView runat="server" CssClass="DataWebControlStyle"> <AlternatingRowStyle CssClass="AlternatingRowStyle" /> <RowStyle CssClass="RowStyle" /> <HeaderStyle CssClass="HeaderStyle" /> <FooterStyle CssClass="FooterStyle" /> <SelectedRowStyle CssClass="SelectedRowStyle" /> </asp:GridView>
Como muestra la captura de pantalla de la figura 12, estos cambios hacen que el pie de pgina se destaca con mayor claridad.
Figura 12. La fila de pie de pgina del GridView ahora tiene un fondo de color rojizo Paso 3. Calculo del resumen de datos Con la visualizacin del pie de pgina del GridView, el siguiente reto al que nos enfrentamos es como calcular el resumen de los datos. Existen dos formas de calcular esta informacin a agregar:
1. Por medio de una consulta SQL que podra emitir una consulta adicional a la base de datos para que calcule el resumen de los datos para una determinada categora. SQL Server incluye un numero de funciones agregadas junto con una clausula GROUP BY para especificar los datos sobre los datos que deben ser sumarizados. La siguiente consulta recuperara la informacin necesaria:
SELECT CategoryID, AVG(UnitPrice), SUM(UnitsInStock), SUM(UnitsOnOrder) FROM Products WHERE CategoryID = categoryID GROUP BY CategoryID
Por supuesto no deseamos emitir esta consulta directamente a la pgina [Link], en su lugar creamos un mtodo en el ProductsTableAdapter y el ProductsBLL. 2. Calcula esta informacin ya que est siendo agregada al GridView como discutimos en el tutorial Formato Personalizado basado en los datos, el controlador de evento RowDataBound del GridView se genera para cada fila agregada al GridView luego que este ha sido enlazado. Creando un controlador de evento para este evento podemos mantener un total actualizado de los valores que deseamos agregar. Despus que la ltima fila de datos ha sido enlazada al GridView tenemos los totales y la informacin necesaria para calcular el promedio. Normalmente empleamos el segundo enfoque, ya que ahorra un viaje a la base de datos y el esfuerzo necesario para implementar la funcionalidad de resumen en la capa de acceso a datos y en la capa lgica de negocios, pero cualquier enfoque es suficiente. Para este tutorial utilizaremos la segunda opcin y realizaremos un seguimiento del total actualizado con el controlador de eventos RowDataBound. Crea un controlador del evento RowDataBound para el GridView, seleccionando el GridView en el diseador, haciendo clic en el icono del rayo en la ventana propiedades y doble clic en el evento RowDataBound. De forma alternativa puede seleccionar el GridView y su evento RowDataBound desde las listas despegables en la parte superior del archivo de la clase de cdigo subyacente de la pgina [Link]. Esto creara un nuevo
Con el fin de mantener un total actualizado necesitamos definir las variables fuera del alcance del controlador de eventos. Cree las siguientes cuatro variables a nivel de pgina: _totalUnitPrice de tipo Decimal _totalNonNullUnitPrice de tipo Integer _totalUnitsInStock de tipo Integer _totalUnitsOnOrder de tipo Integer
Luego escriba el cdigo para incrementar estas tres variables para cada fila de datos encontrada en el controlador de evento RowDataBound.
Dim _totalUnitPrice As Decimal = 0 Dim _totalNonNullUnitPriceCount As Integer = 0 Dim _totalUnitsInStock As Integer = 0 Dim _totalUnitsOnOrder As Integer = 0 Protected Sub ProductsInCategory_RowDataBound _ (sender As Object, e As GridViewRowEventArgs) _ Handles [Link] If [Link] = [Link] Then Dim product As [Link] = _ CType(CType([Link], DataRowView).Row, [Link]) If Not [Link]() Then _totalUnitPrice += [Link] _totalNonNullUnitPriceCount += 1 End If If Not [Link]() Then _totalUnitsInStock += [Link] End If If Not [Link]() Then
_totalUnitsOnOrder += [Link] End If ElseIf [Link] = [Link] Then Dim avgUnitPrice As Decimal = _ _totalUnitPrice / CType(_totalNonNullUnitPriceCount, Decimal) [Link](1).Text = "Avg.: " & [Link]("c") [Link](2).Text = "Total: " & _totalUnitsInStock.ToString() [Link](3).Text = "Total: " & _totalUnitsOnOrder.ToString() End If End Sub
El controlador del evento RowDataBound inicia asegurndose que est tratando con un DataRow. Una vez que se ha establecido, la instancia [Link] que fue enlazada al objeto GridViewRow en [Link] es almacenada en la variable product. Luego las variables totales actualizadas son incrementadas por los valores correspondientes a los productos actuales (asumiendo que no contienen valores de base de datos Null). Hacemos seguimiento del total de UnitPrice actualizado y el nmero de registros noNull UnitPrice porque el precio promedio es el cociente de estos dos nmeros. Paso 4. Mostrar el resumen de datos en el pie de pgina Con el resumen de datos totalizado, el ltimo paso es mostrarlo en la fila de pie de pgina del GridView. Esta tarea tambin puede ser realizada mediante programacin a travs del controlador del evento RowDataBound. Recordemos que el evento RowDataBound se genera para todas las filas que son enlazadas al GridView, incluyendo la fila de pie de pgina. Por lo tanto podemos ampliar nuestro controlador de evento para que muestre los datos en la fila de pie de pgina usando el siguiente cdigo:
Protected Sub ProductsInCategory_RowDataBound _ (sender As Object, e As GridViewRowEventArgs) _ Handles [Link] If [Link] = [Link] Then ... Increment the running totals ... ElseIf [Link] = [Link] ... Display the summary data in the footer ... End If End Sub
Como la fila de pie de pgina es agregada al GridView despus que todas las filas de datos han sido agregadas, podemos estar seguros que en el momento en que vayamos a mostrar el resumen de datos en el pie de pgina, la actualizacin del clculo de los totales ya se ha completado. El ltimo paso es establecer estos valores en las celdas del pie de pgina. Para mostrar el texto en una celda en particular del pie de pgina, usamos [Link](index).Text=value , donde la indexacin de las celdas comienza en 0. El siguiente cdigo calcula el precio promedio (el precio total divido entre el numero de productos) y los muestra junto con el nmero total de unidades en stock y las unidades ordenadas en las celdas apropiadas del pie de pgina del control GridView.
Protected Sub ProductsInCategory_RowDataBound _ (sender As Object, e As GridViewRowEventArgs) _ Handles [Link] If [Link] = [Link] Then ... <i>Increment the running totals</i> ... ElseIf [Link] = [Link] Dim avgUnitPrice As Decimal = _ _totalUnitPrice / CType(_totalNonNullUnitPriceCount, Decimal) [Link](1).Text = "Avg.: " & [Link]("c") [Link](2).Text = "Total: " & _totalUnitsInStock.ToString() [Link](3).Text = "Total: " & _totalUnitsOnOrder.ToString() End If End Sub
La figura 13 muestra el informe luego que se ha agregado el cdigo. Note como el ToString(c) hace que el resumen de la informacin sobre el precio promedio sea formateado como una moneda.
Figura 13. Resumen Visualizar el resumen de los datos es requisito comn en los informes y el control GridView hace que sea fcil incluir esta informacin en su fila de pie de pgina. La fila de pie de pgina se muestra cuando la propiedad ShowFooter del GridView se establece en True y puede tener el texto en sus celdas establecindolo mediante programacin a travs del controlador de eventos del RowDataBound. El clculo del resumen de datos puede hacerse consultando de nuevo a la base de datos o mediante el uso de la clase de cdigo subyacente de la pgina [Link] para calcular mediante programacin el resumen de los datos. Con este tutorial concluimos nuestro examen del Formato personalizado con los controles GridView, DetailsView y FormView. En nuestro siguiente tutorial iniciaremos la exploracin de cmo insertar, actualizar y eliminar datos utilizando estos mismos controles.
16.
En este tutorial veremos cmo asignar los mtodos Insert(), Update() y Delete() del ObjectDataSource a los mtodos de las clases BLL, as como la forma de configurar los controles GridView, FormView y DetailsView para proporcionar la capacidad de modificacin de datos. Introduccin Durante varios de los tutoriales anteriores hemos estudiado la forma de mostrar los datos en una pgina web [Link] por medio de los controles GridView, DetailsView y FormView. Estos controles solo trabajan con los datos que se les suministran. Comnmente estos controles acceden a los datos por medio de un control de origen de datos, como el ObjectDataSource. Hemos visto que el ObjectDataSource acta como un proxy entre la pgina [Link] y los datos subyacentes. Cuando un GridView necesita mostrar datos, invoca el mtodo Select() de su ObjectDataSource, el cual a su vez invoca un mtodo de nuestra capa lgica de negocios (BLL), el cual llama un mtodo del TableAdapter apropiado en la capa de acceso a datos DAL; la cual a su vez enva una consulta SELECT a la base de datos Northwind. Recordemos que cuando creamos los TableAdapters de la DAL en nuestro primer tutorial, Visual Studio agreg automticamente mtodos para insertar, actualizar y eliminar datos desde la tabla de la base de datos subyacente. Por otra parte, en la creacin de la capa lgica de negocios diseamos mtodos en la BLL que llaman a estos mtodos de modificacin de datos de la DAL. Adems de su mtodo Select(), el ObjectDataSource tiene mtodos Insert(), Delete() y Update(). Al igual que el mtodo Select, estos tres mtodos se pueden asignar a los mtodos de un objeto subyacente. Cuando se configuran los controles GridView, FormView y DetailsView para insertar, agregar o actualizar datos, estos proporcionan una interfaz de usuario para modificar los datos subyacentes. Esta interfaz de usuario llama a los mtodos Insert (), Update() y Delete() del ObjectDataSource, que luego invoca a los mtodos asociados del objeto subyacente (Ver Figura 1).
Figura 1. Los mtodos Insert (), Update () y Delete () del ObjectDataSource sirven como proxy en la BLL En este tutorial veremos cmo asignar los mtodos Insert(), Update() y Delete() del ObjectDataSource a los mtodos de las clases en la BLL, as como la forma de configurar los controles GridView, FormView y DetailsView para proporcionar la capacidad de modificacin de datos. Paso 1: Creacin de pginas Web de tutoriales de Insercin, Actualizacin y Eliminacin. Antes de empezar a explorar la forma de insertar, actualizar y eliminar datos, primero tommonos un momento para crear las pginas [Link] que utilizaremos en este y en los prximos tutoriales, para el sitio web de nuestro proyecto. Comencemos agregando una carpeta llamada EditInsertDelete. Luego agregue las siguientes pginas [Link] a la carpeta, asegurndose de asociar cada pgina con la pagina principal [Link]. [Link] [Link] [Link] [Link] [Link] [Link] [Link] [Link]
[Link]
Figura 2. Agregar las pginas [Link] para los tutoriales relacionados con la modificacin de datos Al igual que en las otras carpetas, [Link] en la carpeta EditInsertDelete mostrar una lista de los tutoriales de su seccin. Recordemos que el control de usuario [Link] proporciona esta funcionalidad. Por lo tanto aadimos este control de usuario a [Link], arrastrndolo desde el explorador de soluciones a la vista de diseo de la pgina.
Figura 3. Agregue el control de usuario [Link] a [Link] Por ltimo, agregue las pginas como entradas al archivo [Link]. En concreto agregue el siguiente marcado despus del nodo <SiteMap> de Formato Personalizado:
<siteMapNode title="Editing, Inserting, and Deleting" url="~/EditInsertDelete/[Link]" description="Samples of Reports that Provide Editing, Inserting, and Deleting Capabilities"> <siteMapNode url="~/EditInsertDelete/[Link]" title="Basics" description="Examines the basics of data modification with the GridView, DetailsView, and FormView controls." /> <siteMapNode url="~/EditInsertDelete/[Link]" title="Data Modification Events" description="Explores the events raised by the ObjectDataSource pertinent to data modification." /> <siteMapNode url="~/EditInsertDelete/[Link]" title="Error Handling" description="Learn how to gracefully handle exceptions raised during the data modification workflow." /> <siteMapNode url="~/EditInsertDelete/[Link]" title="Adding Data Entry Validation" description="Help prevent data entry errors by providing validation." /> <siteMapNode url="~/EditInsertDelete/[Link]" title="Customize the User Interface" description="Customize the editing and inserting user interfaces." /> <siteMapNode url="~/EditInsertDelete/[Link]"
title="Optimistic Concurrency" description="Learn how to help prevent simultaneous users from overwritting one another s changes." /> <siteMapNode url="~/EditInsertDelete/[Link]" title="Confirm On Delete" description="Prompt a user for confirmation when deleting a record." /> <siteMapNode url="~/EditInsertDelete/[Link]" title="Limit Capabilities Based on User" description="Learn how to limit the data modification functionality based on the user role or permissions." /> </siteMapNode>
Despus de actualizar el [Link] tmese un tiempo para visualizar el sitio web de tutoriales a travs de un navegador. El men de la izquierda ahora incluye los artculos de los tutoriales de edicin, actualizacin y eliminacin.
Figura 4. El SiteMap ahora incluye entradas a los tutoriales de Edicin, Insercin y Eliminacin Paso2: Agregar y configurar el ObjectDataSource Dado que el GridView, DetailsView y el FormView difieren en su capacidad de modificacin y diseo, examinaremos cada uno de ellos de forma individual. Sin embargo, en lugar de tener cada control usando su propio ObjectDataSource, crearemos un nico ObjectDataSource que los tres controles puedan compartir.
Abra la pgina [Link], arrastre un ObjectDataSource desde el cuadro de herramientas hasta el diseador y haga clic en el enlace Configurar su origen de datos desde su etiqueta inteligente. Como la clase ProductsBLL es la nica clase que utiliza mtodos de edicin, insercin y eliminacin, entonces configuramos el ObjectDataSource para que utilice esta clase.
Figura 5. Configure el ObjectDataSource para utilizar la clase ProductsBLL En la siguiente pantalla, podemos especificar qu mtodos de la clase ProductsBLL se asignan a los mtodos Select(), Update(), Delete() e Insert() del ObjectDataSource, seleccionando la pestaa adecuada y escogiendo el mtodo de la lista desplegable. A estas alturas, la figura 6 debera resultar familiar, asigne el mtodo Select() del ObjectDataSource al mtodo GetProducts() de la clase ProductsBLL. Los mtodos Insert(), Update() y Delete() pueden ser configurados seleccionando la pestaa apropiada de la lista superior.
Figura 6. Haga que el ObjectDataSource devuelva todos los productos Las figuras 7, 8 y 9 muestran las pestaas INSERT, UPDATE y DELETE del ObjectDataSource. Configure estas pestaas para que los mtodos Insert(), Update() y Delete() invoquen los mtodos AddProduct, UpdateProduct y DeleteProduct de la clase ProductsBLL respectivamente.
Figura 7. Asignacin del mtodo Update() del ObjectDataSource al mtodo UpdateProduct de la clase ProductsBLL
Figura 8. Asignacin del mtodo Insert() del ObjectDataSource al mtodo AddProduct de la clase ProductsBLL
Figura 9. Asignacin del mtodo Delete() del ObjectDataSource al mtodo DeleteProduct de la clase ProductsBLL Usted puede haber notado que las listas desplegables de las pestaas UPDATE, INSERT y DELETE ya tenan seleccionados estos mtodos. Esto es gracias a nuestro uso del DataObjectMethodAttribute que decora los mtodos de ProductsBLL. Por ejemplo, el mtodo DeleteProduct tiene la siguiente firma:
<[Link] _ ([Link], True)> _ Public Function DeleteProduct(ByVal productID As Integer) As Boolean End Function
El atributo DataObjectMethodAttribute indica el propsito de cada mtodo, ya sea para seleccionar, insertar, actualizar o eliminar y si este es o no el valor por defecto. Si se omiten estos atributos cuando se crean las clases BLL, tendr que seleccionar manualmente los mtodos desde las pestaas INSERT, UPDATE y DELETE. Asegrese que se asignen los mtodos apropiados de la clase ProductsBLL a los mtodos Insert (), Update () y Delete () del ObjectDataSource y luego haga clic en Finalizar para completar el asistente. Examine el marcado del ObjectDataSource Despus de configurar el ObjectDataSource por medio del asistente, vaya a la vista cdigo fuente para examinar el marcado declarativo generado. La etiqueta <asp:ObjectDataSource> especifica el objeto y los mtodos subyacentes a invocar. Adems hay DeleteParameters, UpdateParameters e InsertParemeters que se asignan a los parmetros de entrada para los mtodos AddProduct, UpdateProduct y DeleteProduct de la clase ProductsBLL
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" DeleteMethod="DeleteProduct" InsertMethod="AddProduct" OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts" TypeName="ProductsBLL" UpdateMethod="UpdateProduct"> <DeleteParameters> <asp:Parameter Name="productID" Type="Int32" /> </DeleteParameters> <UpdateParameters> <asp:Parameter Name="productName" Type="String" /> <asp:Parameter Name="supplierID" Type="Int32" /> <asp:Parameter Name="categoryID" Type="Int32" /> <asp:Parameter Name="quantityPerUnit" Type="String" /> <asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="unitsInStock" Type="Int16" /> <asp:Parameter Name="unitsOnOrder" Type="Int16" /> <asp:Parameter Name="reorderLevel" Type="Int16" /> <asp:Parameter Name="discontinued" Type="Boolean" /> <asp:Parameter Name="productID" Type="Int32" /> </UpdateParameters> <InsertParameters> <asp:Parameter Name="productName" Type="String" /> <asp:Parameter Name="supplierID" Type="Int32" /> <asp:Parameter Name="categoryID" Type="Int32" /> <asp:Parameter Name="quantityPerUnit" Type="String" /> <asp:Parameter Name="unitPrice" Type="Decimal" /> <asp:Parameter Name="unitsInStock" Type="Int16" /> <asp:Parameter Name="unitsOnOrder" Type="Int16" /> <asp:Parameter Name="reorderLevel" Type="Int16" /> <asp:Parameter Name="discontinued" Type="Boolean" /> </InsertParameters> </asp:ObjectDataSource>
El ObjectDataSource incluye un parmetro para cada uno de los parmetros de entrada de sus mtodos asociados; al igual que una lista de SelectParameter est presente cuando el ObjectDataSource es configurado para llamar a un mtodo de seleccin que espera un parmetro de entrada (como GetProductsByCategoryId (categoryID)). Como veremos prontamente los valores de DeleteParameters, UpdateParameters e InsertParameters se establecen automticamente para que el GridView, FormView y DetailsView invoque los mtodos Update(), Insert() y Delete() del ObjectDataSource. Estos valores tambin pueden establecerse mediante programacin si es necesario, como veremos en un futuro tutorial. Uno de los efectos secundarios de usar el asistente para configurar el ObjectDataSource es que Visual Studio establece la propiedad OldValuesParameterFormatString a original {0}. Este valor de la propiedad es usado para incluir los valores originales de los datos que se estn editando y es til en dos situaciones: Si al editar un registro, el usuario es capaz de cambiar la llave principal. En este caso, tanto el nuevo valor de la llave principal y el valor original de la misma, deben ser proporcionados de manera que se pueda encontrar el registro con el valor original de la llave principal y en consecuencia actualizar su valor.
Cuando usamos la concurrencia optimista. La concurrencia optimista es una tcnica para asegurar que dos usuarios simultneos no sobrescriban los cambios del otro, este es un tema que veremos en un tutorial futuro.
La propiedad OldValuesParameterFormatString indica el nombre de los parmetros de entrada para los valores originales en los mtodos Update y Delete del objeto subyacente. Hablaremos con mayor detalle de esta propiedad y de su propsito ms adelante cuando exploremos la concurrencia optimista. Sin embargo, lo traigo ahora, debido a que nuestros mtodos BLL no esperan los valores originales y por lo tanto es importante que eliminemos esta propiedad. en del Si un dejamos valor la propiedad al valor el OldValuesParameterFormatString invocar los mtodos Update() establecida o Delete() diferente ya
predeterminado ({0}), se producir un error cuando el control de datos web intente ObjectDataSource que ObjectDataSource intentar pasar tanto los UpdateParameters o DeleteParameters establecidos, junto con el valor original de los parmetros. Si no est muy claro en este momento, no se preocupe, examinaremos esta propiedad y su funcionalidad en un futuro tutorial. Por ahora solo tenga la certeza de borrar por completo esta declaracin de propiedad de la sintaxis declarativa o establecerla en el valor predeterminado ({0}). Nota: Si solo limpia el valor de la propiedad OldvaluesParameterFormatString desde la ventana propiedades en la vista diseo, la propiedad seguir existiendo en la sintaxis declarativa, pero estableciendo una cadena vaca. Esto por desgracia se traducir en el mismo problema mencionado anteriormente. Por lo tanto quite la propiedad totalmente de la sintaxis declarativa o desde la ventana propiedades establezca el valor predeterminado {0}. Paso 3: Agregue un control de datos Web y configrelo para la modificacin de datos Una vez que aada el ObjectDataSource a la pgina y lo haya configurado, estamos listos para agregar los controles de datos web a la pgina para mostrar los datos y proporcionar un medio al usuario para que pueda modificarlos. Veremos el GridView, FormView y DetailsView separadamente, ya que estos controles de datos web difieren en su capacidad de modificacin de datos y su configuracin.
Como veremos en el resto de este articulo, agregar un soporte bsico de edicin, insercin y eliminacin por medio de los controles GridView, FormView y DetailsView es realmente tan simple como agregar un par de casillas de verificacin. Hay muchas sutilezas y filos en los casos del mundo real que hacen que proporcionar dicha funcionalidad sea ms complicado que simplemente apuntar y hacer clic. Sin embargo, en este tutorial nos centraremos nicamente en demostrar una capacidad sencilla de modificacin de datos. En los tutoriales futuros, estudiaremos los problemas que muy seguramente surgirn en un ambiente del mundo real. Eliminacin de datos desde el GridView Comience arrastrando un GridView desde el cuadro de herramientas hasta el diseador. Luego enlace el ObjectDataSource al GridView seleccionando este en la lista desplegable de la etiqueta inteligente del GridView. Hasta este momento el marcado declarativo del GridView ser el siguiente:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1"> <Columns> <asp:BoundField DataField="ProductID" HeaderText="ProductID" InsertVisible="False" ReadOnly="True" SortExpression="ProductID" /> <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" /> <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" SortExpression="SupplierID" /> <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" SortExpression="CategoryID" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit" SortExpression="QuantityPerUnit" /> <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" SortExpression="UnitPrice" /> <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock" SortExpression="UnitsInStock" /> <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder" SortExpression="UnitsOnOrder" /> <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel" SortExpression="ReorderLevel" /> <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" DataKeyNames="ProductID"
SortExpression="Discontinued" /> <asp:BoundField DataField="CategoryName" HeaderText="CategoryName" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="SupplierName" ReadOnly="True" SortExpression="SupplierName" /> </Columns> </asp:GridView>
Enlazar el ObjectDataSource por medio de su etiqueta inteligente tiene dos ventajas: Los BoundFields y los CheckBoxFields se crean automticamente para cada uno de los campos devueltos por el ObjectDataSource. Por otro lado las propiedades de los BoundFields y los CheckBoxFields son establecidas sobre la base de metadatos del campo subyacente. Por ejemplo los campos ProductID, CategoryName y SupplierName estn marcados como de solo lectura en el ProductsDataTable y por lo tanto no deben ser actualizados durante la edicin. Para adaptarse a esto, se establece la propiedad ReadOnly de estos BoundFields en Verdadero. La propiedad DataKeyNames se asigna al campo(s) de la llave primaria del objeto subyacente. Esto es esencial cuando se utiliza el control GridView para editar o eliminar datos, ya que esta propiedad indica el campo (o conjunto de campos) que identifican de forma nica a cada registro. Para obtener ms informacin sobre la propiedad DataKeyNames, refirase de nuevo al tutorial Reporte Maestro/Detalle usando un GridView maestro seleccionable con un detalle DetailsView. Aunque el GridView puede ser enlazado al ObjectDataSource por medio de la ventana propiedades o por medio de la sintaxis declarativa, hacer ello requiere agregar manualmente los apropiados BoundFields y el marcado del DataKeyNames. El control GridView proporciona un soporte integrado para la edicin y eliminacin a nivel de fila. Configurando un GridView para que soporte la eliminacin, agregamos una columna de botones de eliminacin. Cuando el usuario final, hace clic en el botn eliminar para una fila en particular, se genera una devolucin de datos y el GridView sigue los siguientes pasos:
1. Son asignados los valores DeleteParameters del ObjectDataSource. 2. Es invocado el mtodo Delete() del ObjectDataSource, eliminando el registro especificado. 3. El control GridView se vuelve a enlazar al ObjectDataSource invocando su mtodo Select(). Los valores asignados al DeleteParameters son los valores de los campos
DataKeyNames de la fila en la que se ha dado clic al botn Eliminar. Por lo tanto es vital que la propiedad DataKeyNames del GridView se establezca correctamente. Si esta es perdida, se asignar el valor de Nothing al DeleteParameters en el paso 1, lo que originar que ningn registro sea eliminado en el paso 2. Nota: La coleccin DataKeyNames es almacenada en el control de estado del GridView, lo que significa que los valores DataKeys sern recordados por medio de la devolucin de datos incluso si el ViewState del GridView se ha deshabilitado. Sin embargo es muy importante que el ViewState quede habilitado para los GridViews que soporten eliminacin o actualizacin (el comportamiento por defecto). Si establece la propiedad EnableViewState del GridView en Falso, el comportamiento de edicin y eliminacin funcionan bien para un solo usuario, pero si existen varios usuarios al tiempo eliminando datos, existe la posibilidad que accidentalmente borren o editen los registros de los otros usuarios. Esta misma advertencia se aplica tambin para los DetailsView y los FormViews. Para agregar capacidades de eliminacin en un GridView, simplemente vaya a su etiqueta inteligente y seleccione la opcin Habilitar Eliminacin.
Figura 10. Seleccione la casilla Habilitar Eliminacin Habilitando la opcin Habilitar Eliminacin desde la etiqueta inteligente, se aade un CommandField al GridView. El CommandField presenta una columna con botones en el GridView para realizar una o varias de las siguientes tareas: la seleccin de un registro, la edicin de un registro y la eliminacin de un registro. Anteriormente vimos el CommandField en accin con la seleccin de registros en el tutorial Registros Maestro/Detalles DetailsView. El CommandField contiene una serie de propiedades ShowXButton que indica que serie de botones son mostrados en el CommandField. Al marcar la casilla de verificacin Habilitar Eliminacin, se agrega a la coleccin de columnas del GridView un CommandField cuya propiedad ShowDeleteButton es True.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1"> <Columns> <asp:CommandField ShowDeleteButton="True" /> ... BoundFields removed for brevity ... </Columns> </asp:GridView>
utilizando
un
GridView
Maestro
Seleccionable
un
detalle
Hasta este punto lo creas o no, hemos terminado con la adicin del soporte de eliminacin del GridView! En la figura 11 se muestra que cuando visitamos esta pgina desde un navegador existe una columna de botones Eliminar.
Figura 11. El CommandField agrega una columna de botones Eliminar Si usted ha estado construyendo este tutorial desde el principio por su cuenta, al probar esta pgina haciendo clic en el botn eliminar se producir una excepcin. Contine leyendo porque se presentan estas excepciones y como solucionarlas. Nota: Si usted est siguiendo este tutorial a travs de la descarga que los acompaa, estos problemas ya han sido solucionados. Sin embargo, le animo a leer los detalles que se indican a continuacin para identificar los problemas que pueden surgir y las soluciones adecuadas. Si al intentar eliminar un producto, se obtiene una excepcin cuyo mensaje es similar a ObjectDataSource, ObjectDatSource1 no puede encontrar un mtodo no genrico DeleteProduct que tenga parmetros: ProductID, Original_ProductID, es probable que haya olvidado quitar intentar la propiedad pasar los OldValuesParameterFormatString parmetros de entrada ProductID del y ObjectDataSource. Con la propiedad OldValuesParameterFormatString definida, el ObjectDataSource Original_ProductID al mtodo DeleteProduct. Sin embargo, DeleteProduct solo acepta un nico parmetro de entrada, por lo cual se genera la excepcin. Extrayendo la propiedad OldValuesParameterFormatString (o si se establece en {0}) indicamos al ObjectDataSource que no trate de pasar el valor original al parmetro de entrada.
Figura 12. Asegrese que la propiedad OldValuesParameterFormatString ha sido removida Incluso si ha quitado la propiedad OldValuesParameterFormatString, usted seguir recibiendo una excepcin cuando trate de eliminar un producto, con el mensaje: Conflicto con la sentencia DELETE con la restriccin REFERENCE FK_Order_Details_Products. La base de datos Northwind contiene una restriccin de llave fornea entre la tabla Products y la tabla OrderDetails, lo que significa que un producto no puede ser eliminado del sistema si hay uno o ms registros relacionados con l en la tabla OrderDetails. Puesto que cada producto en la base de datos Northwind tiene al menos un registro en la tabla OrderDetails, no se puede eliminar ninguno de los productos hasta que se eliminen primero los productos asociados con los registros de detalle de orden.
Figura 13. Una restriccin de llave externa prohbe la eliminacin de Productos Para nuestro tutorial eliminaremos todos los registros de la tabla OrderDetails. En una aplicacin del mundo real necesitaramos: Tener otra pantalla para administrar la informacin de los detalles de pedido. Aumentar el mtodo DeleteProduct para incluir la lgica para eliminar los detalles de orden del producto especificado. Modificar la consulta T-SQL utilizada por el TableAdapter para incluir la eliminacin de los detalles de pedidos del producto especificado. Vamos a examinar todos los registros de la tabla OrderDetails para eludir la restriccin de la llave fornea. Vaya al explorador de servidores en Visual Studio, haga clic en el nodo [Link] y seleccione nueva consulta. Luego en la ventana de consulta, ejecute la instruccin SQL: DELETE FROM [OrderDetails]
Figura 14. Eliminar todos los registros de la tabla OrderDetails Despus de limpiar la tabla OrderDetails al hacer clic en el botn Eliminar, se borrar el producto sin ningn error. Si al hacer clic en el botn eliminar no se elimina el producto, asegrese de que la propiedad DataKeysNames se establece en el campo de la llave primaria (productID). Nota: Cuando hacemos clic en el botn Eliminar se produce una devolucin de datos y el registro se elimina. Esto puede ser peligroso ya que es fcil hacer clic accidentalmente en el botn Eliminar de la fila equivocada. En un futuro tutorial veremos cmo agregar una confirmacin del lado del cliente al eliminar un registro. Edicin de datos con el GridView Junto con la eliminacin, el GridView proporciona un soporte integrado de edicin a nivel de fila, configurando un GridView para que soporte la edicin agregamos una columna de botones de edicin. Desde la perspectiva del usuario final, al hacer clic en el botn editar de una fila, hacemos que la fila sea editable, convirtiendo las celdas en TextBox que contienen los valores existentes y remplazando el botn editar por los botones actualizar y cancelar. Despus de realizar los cambios deseados, el usuario puede hacer clic en actualizar para confirmar los cambios o en el botn cancelar para descartarlos. En cualquiera de los casos, despus de hacer clic en los botones actualizar o cancelar, el GridView regresa a su estado anterior a la edicin.
Desde nuestra perspectiva como desarrolladores de pginas, cuando el usuario hace clic en el botn editar de una fila determinada, se produce una devolucin de datos y el GridView realiza los siguientes pasos: 1. La propiedad EditItemIndex del GridView se asigna al ndice de la fila en la que se hace clic en el botn editar. 2. El control GridView vuelve a enlazarse con el ObjectDataSource invocando su mtodo Select (). 3. Se presenta en modo de edicin la fila cuyo ndice coincida con el valor de EditItemIndex. En este modo, el botn editar es sustituido por los botones actualizar y cancelar, adems los BoundFields cuyas propiedades ReadOnly son falsas (por defecto), se presentan como controles TextBox con sus propiedades Text asignadas a los valores de los datos de los campos. Hasta el momento el marcado es devuelto al navegador, permitindole al usuario final hacer los cambios a los datos de la fila. Cuando el usuario hace clic en el botn actualizar, se produce una devolucin de datos y el GridView realiza los siguientes pasos: 1. Los valores UpdateParameters del ObjectDataSource se asignan a los valores finales ingresados por el usuario en la interfaz de edicin del GridView. 2. Se invoca el mtodo Update() del ObjectDataSource y se realiza la actualizacin del registro especificado. 3. El control GridView se vuelve a enlazar con el ObjectDataSource invocando su mtodo Select(). Los valores de la llave principal asignados al UpdateParameters en el paso 1, proceden de los valores especificados en la propiedad DataKeysNames, mientras que los valores de las llaves no primarias provienen del texto en los controles TextBox de la fila editada. En la eliminacin es vital que la propiedad DataKeysNames del GridView se establezca correctamente, ya que si esta es perdida, se asignara un valor de Nothing al valor de la llave principal en el UpdateParameters en el paso 1, lo que dar lugar a que ningn registro sea actualizado en el paso 2. La funcin de edicin se puede activar sencillamente seleccionando la casilla Habilitar edicin en la etiqueta inteligente del GridView.
Figura 15. Seleccin de la casilla Habilitar Edicin Seleccionando la casilla Habilitar Edicin, se agregara un CommandField (si es necesario) y se establece su propiedad ShowEditButton en True. Como vimos anteriormente, el CommandField contiene una serie de propiedades ShowXButton que indican la serie de botones que son mostrados en el CommandField. Seleccionando la casilla Habilitar Edicin agregamos la propiedad ShowEditButton al CommandField actual:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1"> <Columns> <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" /> ... BoundFields removed for brevity ... </Columns> </asp:GridView>
Esto es todo lo que hay que hacer para agregar un soporte de edicin rudimentario. Como muestra la figura 16, la interfaz de edicin es bastante simple, cada Boundfield cuya propiedad ReadOnly se establezca en True (por defecto), se representa como un cuadro de texto. Esto incluye campos como CategoryID y SupplierID, que son claves para otras tablas.
Figura 16. Haciendo clic en el botn editar de Chao mostramos la fila en modo de edicin Adems de solicitar a los usuarios que editen directamente los valores de las llaves forneas, la interfaz de edicin tiene falencias en las siguientes formas: 1. Si es usuario ingresa un CategoryID o un SupplierID que no existe en la base de datos, el UPDATE violar una restriccin de llave externa, generando una emisin de excepcin. 2. La interfaz de edicin no incluye ningn tipo de validacin. Si usted no proporciona un valor requerido, por ejemplo ProductName o escribe un valor de cadena donde se espera un valor numrico, por ejemplo al introducir Demasiado en el cuadro de texto UnitPrice, se emitir una excepcin. En un tutorial futuro examinaremos como agregar controles de validacin a la interfaz de edicin del usuario. 3. En la actualidad, todos los campos que no son de lectura, deben ser incluidos en el GridView. Si tuviramos que quitar un campo del GridView, por ejemplo UnitPrice, al actualizar los datos del GridView no se establecera el valor de UnitPrice en el UpdateParameters, lo cual cambiaria el valor UnitPrice del registro de la base de datos a Null. Del mismo modo, si un campo obligatorio como ProductName se quita del GridView, la actualizacin fallara por la excepcin Columna ProductName no permite valores nulos mencionada anteriormente. 4. El formato de la interfaz de edicin deja mucho que desear. El UnitPrice se muestra con cuatro decimales. Lo ideal sera que los valores de CategoryID y SupplierID tengan dos DropDownLists que muestren una lista de categoras y proveedores del sistema.
Todas estas deficiencias son con las que tendremos que vivir por ahora, pero se abordaran en tutoriales futuros. Insertar, Editar y Eliminar datos con el DetailsView Como hemos visto en los tutoriales anteriores, el control DetailsView muestra un registro a la vez y al igual que el GridView permite la edicin y eliminacin del registro que muestra. Tanto la experiencia del usuario final con los elementos de edicin y eliminacin del DetailsView, como el flujo de trabajo desde el lado de [Link] es idntica a la del GridView. Donde el DetailsView difiere del GridView es en el soporte predefinido de edicin proporcionado. Para mostrar las capacidades de modificacin de datos del DetailsView, comencemos agregando un DetailsView a la pgina [Link] encima del GridView existente y enlcelo con el ObjectDataSource vigente por medio de las etiquetas inteligentes del DetailsView. Luego limpie las propiedades Height y Width del DetailsView y seleccione la opcin Habilitar paginacin en la etiqueta inteligente. Para habilitar el soporte de edicin, actualizacin y eliminacin, simplemente seleccione las opciones Habilitar Edicin, Habilitar insercin y Habilitar eliminacin en la etiqueta inteligente.
Figura 17. Configurar el soporte de edicin, actualizacin y eliminacin del DetailsView Al igual que con el GridView, agregando el soporte de edicin, actualizacin y eliminacin; agregamos un CommandField al DetailsView como se muestra en la siguiente sintaxis declarativa:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False"
DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True"> <Fields> <asp:BoundField DataField="ProductID" HeaderText="ProductID" InsertVisible="False" ReadOnly="True" SortExpression="ProductID" /> <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" /> <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" SortExpression="SupplierID" /> <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" SortExpression="CategoryID" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit" SortExpression="QuantityPerUnit" /> <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" SortExpression="UnitPrice" /> <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock" SortExpression="UnitsInStock" /> <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder" SortExpression="UnitsOnOrder" /> <asp:BoundField DataField="ReorderLevel" HeaderTe