Información general sobre la inserción, actualización y eliminación de datos (C#)

por Scott Mitchell

Descargar PDF

En este tutorial veremos cómo asignar métodos Insert(), Update() y Delete() de ObjectDataSource a los métodos de las clases BLL, así como a configurar los controles GridView, DetailsView y FormView para proporcionar funcionalidades de modificación de datos.

Introducción

En los últimos tutoriales hemos examinado cómo mostrar datos en una página de ASP.NET mediante los controles GridView, DetailsView y FormView. Estos controles simplemente funcionan con los datos proporcionados. Normalmente, estos controles acceden a los datos mediante el uso de un control de origen de datos, como ObjectDataSource. Hemos visto cómo objectDataSource actúa como proxy entre la página ASP.NET y los datos subyacentes. Cuando un GridView necesita mostrar datos, invoca el método Select() de ObjectDataSource, que a su vez invoca un método de nuestra Capa de Lógica Empresarial (BLL), que llama a un método del TableAdapter de la Capa de Acceso a Datos (DAL) correspondiente, que a su vez envía una consulta SELECT a la base de datos Northwind.

Recordemos que cuando creamos los TableAdapters en la DAL en nuestro primer tutorial, Visual Studio agregó automáticamente métodos para insertar, actualizar y eliminar datos de la tabla de la base de datos subyacente. Además, en Creación de una capa de lógica empresarial diseñamos métodos en la BLL que llamaban a estos métodos DAL de modificación de datos.

Además de su método Select(), el ObjectDataSource también tiene los métodos Insert(), Update() y Delete(). Al igual que el método Select(), estos tres métodos pueden asignarse a métodos de un objeto subyacente. Cuando se configura para insertar, actualizar o eliminar datos, los controles GridView, DetailsView y FormView proporcionan una interfaz de usuario para modificar los datos subyacentes. Esta interfaz de usuario llama a los métodos Insert(), Update() y Delete() del ObjectDataSource, que a su vez invocan a los métodos asociados del objeto subyacente (véase la figura 1).

The ObjectDataSource's Insert(), Update(), and Delete() Methods Serve as a Proxy into the BLL

Figura 1: Los métodos Insert(), Update() y Delete() del ObjectDataSource sirven de proxy al BLL (Haga clic para ver la imagen a tamaño completo)

En este tutorial veremos cómo asignar los métodos Insert(), Update() y Delete() del ObjectDataSource a métodos de clases de la BLL, así como cómo configurar los controles GridView, DetailsView y FormView para proporcionar capacidades de modificación de datos.

Paso 1: Crear las páginas web insertar, actualizar y eliminar tutoriales

Antes de empezar a explorar cómo insertar, actualizar y eliminar datos, primero se tardará un momento en crear las páginas de ASP.NET en nuestro proyecto de sitio web que necesitaremos para este tutorial y los siguientes varios. Empiece agregando una nueva carpeta denominada EditInsertDelete. Después, agregue las siguientes páginas ASP.NET a esa carpeta, asegurándose de asociar cada página a la página maestra Site.master:

  • Default.aspx
  • Basics.aspx
  • DataModificationEvents.aspx
  • ErrorHandling.aspx
  • UIValidation.aspx
  • CustomizedUI.aspx
  • OptimisticConcurrency.aspx
  • ConfirmationOnDelete.aspx
  • UserLevelAccess.aspx

Add the ASP.NET Pages for the Data Modification-Related Tutorials

Figura 2: Agregar las páginas de ASP.NET para los tutoriales relacionados con la modificación de datos

Igual que en las otras carpetas, Default.aspx en la carpeta EditInsertDelete enumerará los tutoriales en su sección. Recuerde que el control de usuario SectionLevelTutorialListing.ascx proporciona esta funcionalidad. Por lo tanto, agregue este control de usuario a Default.aspx arrastrándolo desde el Explorador de soluciones a la vista Diseño de la página.

Add the SectionLevelTutorialListing.ascx User Control to Default.aspx

Figura 3: Agregue el control de usuario SectionLevelTutorialListing.ascx a Default.aspx (haga clic aquí para ver la imagen a tamaño completo)

Por último, agregue las siguientes páginas como entradas al archivo Web.sitemap. En concreto, agregue el marcado siguiente después del formato personalizado <siteMapNode>:

<siteMapNode title="Editing, Inserting, and Deleting"
    url="~/EditInsertDelete/Default.aspx"
    description="Samples of Reports that Provide Editing, Inserting,
                  and Deleting Capabilities">
    <siteMapNode url="~/EditInsertDelete/Basics.aspx"
        title="Basics"
        description="Examines the basics of data modification with the
                      GridView, DetailsView, and FormView controls." />
    <siteMapNode url="~/EditInsertDelete/DataModificationEvents.aspx"
        title="Data Modification Events"
        description="Explores the events raised by the ObjectDataSource
                      pertinent to data modification." />
    <siteMapNode url="~/EditInsertDelete/ErrorHandling.aspx"
        title="Error Handling"
        description="Learn how to gracefully handle exceptions raised
                      during the data modification workflow." />
    <siteMapNode url="~/EditInsertDelete/UIValidation.aspx"
        title="Adding Data Entry Validation"
        description="Help prevent data entry errors by providing validation." />
    <siteMapNode url="~/EditInsertDelete/CustomizedUI.aspx"
        title="Customize the User Interface"
        description="Customize the editing and inserting user interfaces." />
    <siteMapNode url="~/EditInsertDelete/OptimisticConcurrency.aspx"
        title="Optimistic Concurrency"
        description="Learn how to help prevent simultaneous users from
                      overwritting one another s changes." />
    <siteMapNode url="~/EditInsertDelete/ConfirmationOnDelete.aspx"
        title="Confirm On Delete"
        description="Prompt a user for confirmation when deleting a record." />
    <siteMapNode url="~/EditInsertDelete/UserLevelAccess.aspx"
        title="Limit Capabilities Based on User"
        description="Learn how to limit the data modification functionality
                      based on the user role or permissions." />
</siteMapNode>

Después de actualizar Web.sitemap, dedique un momento a ver el sitio web de tutoriales a través de un explorador. El menú de la izquierda ahora incluye elementos para la edición, inserción y eliminación de tutoriales.

The Site Map Now Includes Entries for the Editing, Inserting, and Deleting Tutorials

Figura 4: El mapa del sitio incluye ahora entradas para los tutoriales de edición, inserción y eliminación

Paso 2: Agregar y configurar el control de ObjectDataSource

Dado que GridView, DetailsView y FormView difieren en sus funcionalidades y diseño de modificación de datos, vamos a examinar cada uno individualmente. En lugar de tener cada control con su propio ObjectDataSource, vamos a crear un único ObjectDataSource que los tres ejemplos de control pueden compartir.

Abra la página Basics.aspx, arrastre ObjectDataSource desde el cuadro de herramientas al diseñador y haga clic en el vínculo Configurar origen de datos desde su etiqueta inteligente. Dado que la ProductsBLL es la única clase BLL que proporciona métodos de edición, inserción y eliminación, configure el ObjectDataSource para que utilice esta clase.

Configure the ObjectDataSource to Use the ProductsBLL Class

Figura 5: Configurar ObjectDataSource para usar la clase ProductsBLL (Haga clic para ver la imagen de tamaño completo)

En la siguiente pantalla podemos especificar qué métodos de la clase ProductsBLL se asignan a las clases Select(), Insert(), Update() y Delete() del ObjectDataSource seleccionando la pestaña correspondiente y eligiendo el método en la lista desplegable. La figura 6, que ya debería resultarle familiar, asigna el método Select() de ObjectDataSource al método GetProducts() de la clase ProductsBLL. Los métodos Insert(), Update() y Delete() pueden configurarse seleccionando la pestaña correspondiente de la lista situada en la parte superior.

Have the ObjectDataSource Return All of the Products

Figura 6: Hacer que el ObjectDataSource devuelva todos los productos (Haga clic para ver la imagen a tamaño completo)

Las figuras 7, 8 y 9 muestran las pestañas UPDATE, INSERT y DELETE de ObjectDataSource. Configure estas pestañas para que los métodos Insert(), Update() y Delete() invoquen a los métodos UpdateProduct, AddProduct y DeleteProduct de la clase ProductsBLL, respectivamente.

Map the ObjectDataSource's Update() Method to the ProductBLL Class's UpdateProduct Method

Figura 7: Asignar el método Update() del ObjectDataSource a la ProductBLL de la clase UpdateProduct (Haga clic para ver la imagen a tamaño completo)

Map the ObjectDataSource's Insert() Method to the ProductBLL Class's AddProduct Method

Figura 8: Asignar el método Insert() del ObjectDataSource a la ProductBLL de la clase Agregar Product (Haga clic para ver la imagen a tamaño completo)

Map the ObjectDataSource's Delete() Method to the ProductBLL Class's DeleteProduct Method

Figura 9: Asignar el método Delete() del ObjectDataSource a la ProductBLL de la clase DeleteProduct (Haga clic para ver la imagen a tamaño completo)

Es posible que haya observado que las listas desplegables de las pestañas UPDATE, INSERT y DELETE ya tenían estos métodos seleccionados. Esto es gracias a nuestro uso del DataObjectMethodAttribute que decora los métodos del ProductsBLL. Por ejemplo, el método DeleteProduct tiene la siguiente firma:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct(int productID)
{
    ...
}

El atributo DataObjectMethodAttribute indica el propósito de cada método, si es para seleccionar, insertar, actualizar o eliminar y si es o no el valor predeterminado. Si omite estos atributos al crear las clases BLL, deberá seleccionar manualmente los métodos de las pestañas UPDATE, INSERT y DELETE.

Tras asegurarse de que los métodos ProductsBLL apropiados se asignan a los métodos Insert(), Update() y Delete() del ObjectDataSource, haga clic en Finalizar para completar el asistente.

Examen del marcado de ObjectDataSource

Después de configurar ObjectDataSource a través de su asistente, vaya a la vista Origen para examinar el marcado declarativo generado. La etiqueta <asp:ObjectDataSource> especifica el objeto subyacente y los métodos que se van a invocar. Además, hay DeleteParameters, UpdateParameters y InsertParameters que corresponden a los parámetros de entrada para los métodos 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 parámetro para cada uno de los parámetros de entrada de sus métodos asociados, al igual que una lista de SelectParameter está presente cuando el ObjectDataSource está configurado para llamar a un método select que espera un parámetro de entrada (como GetProductsByCategoryID(categoryID)). Como veremos en breve, GridView, DetailsView y FormView establecen automáticamente los valores para estos DeleteParameters, UpdateParameters y InsertParameters antes de invocar el método Insert(), Update() o Delete() de ObjectDataSource. Estos valores también se pueden establecer mediante programación según sea necesario, ya que analizaremos en un tutorial futuro.

Un efecto secundario de utilizar el asistente para configurar a ObjectDataSource es que Visual Studio establece la propiedad OldValuesParameterFormatString en original_{0}. Este valor de propiedad se usa para incluir los valores originales de los datos que se están editando y es útil en dos escenarios:

  • Si, al editar un registro, los usuarios pueden cambiar el valor de clave principal. En este caso, se deben proporcionar tanto el nuevo valor de clave principal como el valor de clave principal original para que se pueda encontrar el registro con el valor de clave principal original y que su valor se actualice en consecuencia.
  • Al usar la simultaneidad optimista. La simultaneidad optimista es una técnica para asegurarse de que dos usuarios simultáneos no sobrescriben los cambios de otro y es el tema de un tutorial futuro.

La propiedad OldValuesParameterFormatString indica el nombre de los parámetros de entrada en los métodos update y delete del objeto subyacente para los valores originales. Analizaremos esta propiedad y su propósito con más detalle al explorar la simultaneidad optimista. Sin embargo, lo menciono ahora porque los métodos de nuestro BLL no esperan los valores originales y, por lo tanto, es importante que eliminemos esta propiedad. Si se deja la propiedad OldValuesParameterFormatString con un valor distinto del predeterminado ({0}), se producirá un error cuando un control web de datos intente invocar los métodos Update() o Delete() de ObjectDataSource, ya que este intentará pasar tanto los parámetros UpdateParameters o DeleteParameters especificados como el valor original.

Si esto no está muy claro en este momento, no se preocupe, examinaremos esta propiedad y su utilidad en un futuro tutorial. Por ahora, simplemente asegúrese de eliminar esta declaración de propiedad completamente de la sintaxis declarativa o establecer el valor en el valor predeterminado ({0}).

Nota:

Si simplemente borra el valor OldValuesParameterFormatString de propiedad de la ventana Propiedades en la vista Diseño, la propiedad seguirá existiendo en la sintaxis declarativa, pero se establecerá en una cadena vacía. Esto, desafortunadamente, seguirá teniendo como resultado el mismo problema descrito anteriormente. Por lo tanto, quite la propiedad por completo de la sintaxis declarativa o, de la ventana Propiedades, establezca el valor en el valor predeterminado, {0}.

Paso 3: Agregar un control web de datos y configurarlo para la modificación de datos

Una vez que objectDataSource se ha agregado a la página y configurado, estamos listos para agregar controles web de datos a la página para mostrar los datos y proporcionar un medio para que el usuario final lo modifique. Veremos por separado GridView, DetailsView y FormView, ya que estos controles web de datos difieren en sus funcionalidades y configuración de modificación de datos.

Como veremos en el resto de este artículo, agregar compatibilidad básica de edición, inserción y eliminación a través de los controles GridView, DetailsView y FormView es realmente tan sencillo como comprobar un par de casillas. Hay muchas sutilezas y casos perimetrales en el mundo real que hacen que proporcionar esa funcionalidad sea más implicada que simplemente apuntar y hacer clic. Sin embargo, este tutorial se centra únicamente en probar las funcionalidades de modificación de datos simplistas. Los tutoriales futuros examinarán las preocupaciones que sin duda surgirán en un entorno real.

Eliminar datos de GridView

Para empezar, arrastre un control GridView desde el cuadro de herramientas al Diseñador. A continuación, enlace ObjectDataSource a GridView seleccionándolo en la lista desplegable de la etiqueta inteligente de GridView. En este momento, el marcado declarativo de GridView será:

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" 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" 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 GridView a ObjectDataSource a través de su etiqueta inteligente tiene dos ventajas:

  • BoundFields y CheckBoxFields se crean automáticamente para cada uno de los campos devueltos por ObjectDataSource. Además, las propiedades BoundField y CheckBoxField se establecen en función de los metadatos del campo subyacente. Por ejemplo, los campos ProductID, CategoryName y SupplierName están marcados como de solo lectura en el ProductsDataTable y, por tanto, no deberían poder actualizarse durante la edición. Para ello, las propiedades ReadOnly de estos BoundFields se establecen en true.
  • La propiedad DataKeyNames se asigna al campo o campos de clave primaria del objeto subyacente. Esto es esencial cuando se usa GridView para editar o eliminar datos, ya que esta propiedad indica el campo (o conjunto de campos) que identifica cada registro. Para obtener más información sobre la propiedad DataKeyNames, consulte de nuevo el tutorial Maestro/Detalle Uso de una GridView maestra seleccionable con una DetailView de detalles.

Aunque GridView se puede vincular a ObjectDataSource a través de la ventana Propiedades o de la sintaxis declarativa, para hacerlo es necesario añadir manualmente las marcas BoundField y DataKeyNames adecuadas.

El control GridView proporciona compatibilidad integrada para la edición y eliminación de nivel de fila. La configuración de GridView para admitir la eliminación agrega una columna de botones Eliminar. Cuando el usuario final hace clic en el botón Eliminar de una fila determinada, se produce una devolución de entrada y GridView realiza los pasos siguientes:

  1. Se asignan los valores DeleteParameters de ObjectDataSource
  2. Se invoca el método Delete() del ObjectDataSource, eliminando el registro especificado
  3. GridView se vuelve a vincular al ObjectDataSource invocando su método Select()

Los valores asignados a los DeleteParameters son los valores de los campos DataKeyNames de la fila cuyo botón Eliminar se ha pulsado. Por lo tanto, es vital que la propiedad DataKeyNames de un GridView esté correctamente configurada. Si falta, al DeleteParameters se le asignará un valor null en el paso 1, lo que a su vez no dará lugar a ningún registro eliminado en el paso 2.

Nota:

La colección DataKeys se almacena en el estado de control del GridView, lo que significa que los valores DataKeys se recordarán a través de postback incluso si el estado de vista de GridView se ha desactivado. Sin embargo, es muy importante que el estado de vista permanezca habilitado para GridViews que admite la edición o eliminación (el comportamiento predeterminado). Si establece la propiedad EnableViewState de GridView en false, el comportamiento de edición y eliminación funcionará correctamente para un único usuario, pero si hay usuarios concurrentes eliminando datos, existe la posibilidad de que estos usuarios concurrentes eliminen o editen accidentalmente registros que no tenían previsto.

Esta misma advertencia también se aplica a DetailsViews y FormViews.

Para agregar funcionalidades de eliminación a GridView, simplemente vaya a su etiqueta inteligente y active la casilla Habilitar eliminación.

Check the Enable Deleting Checkbox

Figura 10: Activar la casilla Habilitar eliminación

Al activar la casilla Habilitar eliminación de la etiqueta inteligente, se agrega un CommandField a GridView. CommandField representa una columna en GridView con botones para realizar una o varias de las tareas siguientes: seleccionar un registro, editar un registro y eliminar un registro. Anteriormente vimos el CommandField en acción con la selección de registros en el tutorial Maestro/Detalle Uso de una GridView maestra seleccionable con una DetailView de detalles.

El CommandField contiene una serie de propiedades ShowXButton que indican qué serie de botones se muestran en el CommandField. Al marcar la casilla Habilitar eliminación se ha añadido un CommandField cuya propiedad ShowDeleteButton es true a la colección Columnas de GridView.

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="ObjectDataSource1">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" />
        ... BoundFields removed for brevity ...
    </Columns>
</asp:GridView>

En este punto, lo crea o no, ¡hemos terminado de añadir soporte de eliminación a GridView! Como se muestra en la figura 11, al visitar esta página a través de un explorador hay una columna de botones Eliminar.

The CommandField Adds a Column of Delete Buttons

Figura 11: CommandField agrega una columna de botones de eliminación (Haga clic para ver la imagen a tamaño completo)

Si ha creado este tutorial desde cero por su cuenta, al probar esta página al hacer clic en el botón Eliminar generará una excepción. Siga leyendo para obtener información sobre por qué se generaron estas excepciones y cómo corregirlas.

Nota:

Si sigue con la descarga que acompaña a este tutorial, estos problemas ya se han tenido en cuenta. Sin embargo, le recomendamos que lea los detalles que se enumeran a continuación para ayudar a identificar problemas que pueden surgir y soluciones alternativas adecuadas.

Si, al intentar eliminar un producto, obtiene una excepción cuyo mensaje es similar a "ObjectDataSource 'ObjectDataSource1' no pudo encontrar un método no genérico 'DeleteProduct' que tenga los parámetros: productID, original_ProductID", es probable que haya olvidado eliminar la propiedad OldValuesParameterFormatString de ObjectDataSource. Con la propiedad OldValuesParameterFormatString especificada, ObjectDataSource intenta pasar los parámetros de entrada productID y original_ProductID al método DeleteProduct. DeleteProduct, sin embargo, solo acepta un único parámetro de entrada, de ahí la excepción. Eliminar la propiedad OldValuesParameterFormatString (o establecerla en {0}) indica a ObjectDataSource que no intente pasar el parámetro de entrada original.

Ensure That the OldValuesParameterFormatString Property Has Been Cleared Out

Figura 12: Asegurarse de que la propiedad OldValuesParameterFormatString se ha borrado (Haga clic para ver la imagen a tamaño completo)

Aunque hubiera eliminado la propiedad OldValuesParameterFormatString, seguirá obteniendo una excepción al intentar eliminar un producto con el mensaje "La instrucción DELETE entró en conflicto con la restricción REFERENCE 'FK_Order_Details_Products'". La base de datos Northwind contiene una restricción de clave externa entre las tablas Order Details y Products, lo que significa que un producto no puede eliminarse del sistema si existen uno o más registros del mismo en la tabla Order Details. Dado que cada producto de la base de datos Northwind tiene al menos un registro en Order Details, no podemos eliminar ningún producto hasta que no eliminemos primero los registros de detalles del pedido asociados al producto.

A Foreign Key Constraint Prohibits the Deletion of Products

Figura 13: Una restricción de clave externa prohíbe la eliminación de productos (Haga clic para ver la imagen a tamaño completo)

Para nuestro tutorial, vamos a eliminar todos los registros de la tabla Order Details. En una aplicación real, tendríamos que hacer lo siguiente:

  • Tener otra pantalla para administrar la información de detalles del pedido
  • Aumentar el método DeleteProduct para incluir lógica para eliminar los detalles del pedido del producto especificado
  • Modificar la consulta SQL utilizada por el TableAdapter para incluir la eliminación de los detalles del pedido del producto especificado.

Vamos a eliminar todos los registros de la tabla Order Details para eludir la restricción de clave externa. Vaya al Explorador de servidores en Visual Studio, haga clic con el botón derecho en el nodo NORTHWND.MDF y seleccione Nueva consulta. A continuación, en la ventana de consulta, ejecute la siguiente instrucción SQL: DELETE FROM [Order Details]

Delete All Records from the Order Details Table

Figura 14: Eliminar todos los registros de la tabla Order Details (Haga clic para ver la imagen a tamaño completo)

Tras vaciar la tabla Order Details, al hacer clic en el botón Eliminar se borrará el producto sin error. Si al hacer clic en el botón Eliminar no se elimina el producto, compruebe que la propiedad DataKeyNames de GridView está establecida en el campo de clave principal (ProductID).

Nota:

Al hacer clic en el botón Eliminar, se produce una devolución de entrada y se elimina el registro. Esto puede ser peligroso, ya que es fácil hacer clic accidentalmente en el botón Eliminar de la fila incorrecta. En un tutorial futuro veremos cómo agregar una confirmación del lado cliente al eliminar un registro.

Edición de datos con GridView

Junto con la eliminación, el control GridView también proporciona compatibilidad integrada de edición de nivel de fila. La configuración de GridView para admitir la edición agrega una columna de botones Editar. Desde la perspectiva del usuario final, hacer clic en el botón Editar de una fila hace que esa fila se pueda editar, convirtiendo las celdas en cuadros de texto que contienen los valores existentes y reemplazando el botón Editar por los botones Actualizar y Cancelar. Después de realizar los cambios deseados, el usuario final puede hacer clic en el botón Actualizar para confirmar los cambios o el botón Cancelar para descartarlos. En cualquier caso, después de hacer clic en Actualizar o Cancelar GridView vuelve a su estado de edición previa.

Desde nuestra perspectiva como desarrollador de páginas, cuando el usuario final hace clic en el botón Editar de una fila determinada, se produce una devolución de postback y GridView realiza los pasos siguientes:

  1. La propiedad EditItemIndex de GridView se asigna al índice de la fila cuyo botón Editar se ha pulsado
  2. GridView se vuelve a vincular al ObjectDataSource invocando su método Select()
  3. El índice de la fila que coincide con el EditItemIndex se representa en "modo edición". En este modo, el botón Editar se sustituye por los botones Actualizar y Cancelar y los BoundFields cuyas propiedades ReadOnly son False (el valor predeterminado) se renderizan como controles web TextBox cuyas propiedades Text se asignan a los valores de los campos de datos.

En este momento, el marcado se devuelve al explorador, lo que permite al usuario final realizar cambios en los datos de la fila. Cuando el usuario hace clic en el botón Actualizar, se produce un postback y GridView realiza los pasos siguientes:

  1. A los valores UpdateParameters de ObjectDataSource se les asignan los valores introducidos por el usuario final en la interfaz de edición de GridView
  2. Se invoca el método Update() de ObjectDataSource, actualizando el registro especificado.
  3. GridView se vuelve a vincular al ObjectDataSource invocando su método Select()

Los valores de clave principal asignados al UpdateParameters en el paso 1 proceden de los valores especificados en la propiedad DataKeyNames, mientras que los valores de clave no principal proceden del texto de los controles Web TextBox de la fila editada. Al igual que al eliminar, es vital que la propiedad DataKeyNames de un GridView esté correctamente configurada. Si falta, al valor UpdateParameters de la clave principal se le asignará un valor null en el paso 1, lo que a su vez no dará lugar a ningún registro actualizado en el paso 2.

La funcionalidad de edición se puede activar simplemente activando la casilla Habilitar edición en la etiqueta inteligente de GridView.

Check the Enable Editing Checkbox

Figura 15: Activar la casilla Habilitar edición

Al marcar la casilla Habilitar edición se añadirá un CommandField (si es necesario) y se establecerá su propiedad ShowEditButton en true. Como vimos anteriormente, el CommandField contiene una serie de propiedades ShowXButton que indican qué serie de botones se muestran en el CommandField. Al marcar la casilla Habilitar edición se añade la propiedad ShowEditButton al CommandField existente:

<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>

Eso es todo lo que hay que hacer para agregar compatibilidad con la edición rudimentaria. Como muestra la Figura16, la interfaz de edición es bastante básica, cada BoundField cuya propiedad ReadOnly se establece en false (el valor predeterminado) se representa como un TextBox. Esto incluye campos como CategoryID y SupplierID, que son claves de otras tablas.

Clicking Chai s Edit Button Displays the Row in Edit Mode

Figura 16: Al pulsar el botón de edición de Chai se muestra la fila en modo de edición (Haga clic para ver la imagen a tamaño completo)

Además de pedir a los usuarios que editen directamente los valores de clave externa, la interfaz de edición presenta las siguientes carencias:

  • Si el usuario introduce un CategoryID o SupplierID que no existe en la base de datos, el UPDATE violará una restricción de clave externa, provocando una excepción.
  • La interfaz de edición no incluye ninguna validación. Si no proporciona un valor requerido (como ProductName), o introduce un valor de cadena donde se espera un valor numérico (como introducir "¡Demasiado!" en la caja de texto UnitPrice), se lanzará una excepción. En un tutorial futuro se examinará cómo agregar controles de validación a la interfaz de usuario de edición.
  • Actualmente, todos los campos de producto que no sean de solo lectura deben incluirse en GridView. Si elimináramos un campo de GridView, digamos UnitPrice, al actualizar los datos GridView no establecería el valor UnitPriceUpdateParameters, lo que cambiaría el UnitPrice del registro de la base de datos a un valor NULL. Del mismo modo, si se elimina de GridView un campo obligatorio, como ProductName, la actualización fallará con la misma excepción "Column 'ProductName' does not allow nulls" mencionada anteriormente.
  • El formato de la interfaz de edición deja mucho que desear. El UnitPrice se muestra con cuatro decimales. Lo ideal sería que los valores CategoryID y SupplierID contuvieran listas desplegables que enumeraran las categorías y los proveedores del sistema.

Estas son todas las deficiencias con las que tendremos que vivir por ahora, pero se abordarán en futuros tutoriales.

Inserción, edición y eliminación de datos con DetailsView

Como hemos visto en los tutoriales anteriores, el control DetailsView muestra un registro cada vez y, como GridView, permite editar y eliminar el registro mostrado actualmente. Tanto la experiencia del usuario final con la edición y eliminación de elementos de un DetailsView como el flujo de trabajo desde el lado ASP.NET son idénticos a los de GridView. En lo que difiere DetailsView de GridView es en que también proporciona compatibilidad integrada con la inserción.

Para demostrar las funcionalidades de modificación de datos de GridView, comience agregando un DetailsView a la página Basics.aspx por encima del GridView existente y vincúlelo al ObjectDataSource existente a través de la etiqueta inteligente de DetailsView. A continuación, elimine las propiedades Height y Width de DetailsView y active la opción Activar paginación de la etiqueta inteligente. Para habilitar la edición, inserción y eliminación de soporte técnico, simplemente active las casillas Habilitar edición, Habilitar inserción y Habilitar eliminación en la etiqueta inteligente.

Screenshot showing the DetailsView Tasks window with the Enable Inserting, Enable Editing, and Enable Deleting checkboxes selected.

Figura 17: Configurar DetailsView para admitir la edición, inserción y eliminación

Al igual que con GridView, agregar compatibilidad con edición, inserción o eliminación agrega un CommandField a DetailsView, como se muestra en la sintaxis declarativa siguiente:

<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"
            HeaderText="ReorderLevel" SortExpression="ReorderLevel" />
        <asp:CheckBoxField DataField="Discontinued"
            HeaderText="Discontinued" SortExpression="Discontinued" />
        <asp:BoundField DataField="CategoryName"
            HeaderText="CategoryName" ReadOnly="True"
            SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName"
            HeaderText="SupplierName" ReadOnly="True"
            SortExpression="SupplierName" />
        <asp:CommandField ShowDeleteButton="True"
            ShowEditButton="True" ShowInsertButton="True" />
    </Fields>
</asp:DetailsView>

Tenga en cuenta que para DetailsView el CommandField aparece al final de la colección Columns de forma predeterminada. Dado que los campos de DetailsView se representan como filas, CommandField aparece como una fila con los botones Insertar, Editar y Eliminar en la parte inferior de DetailsView.

Screenshot of the DetailsView with the CommandField appearing as a bottom row with Insert, Edit, and Delete buttons.

Figura 18: Configurar la vista detallada para permitir editar, insertar y eliminar (Haga clic para ver la imagen a tamaño completo)

Al hacer clic en el botón Eliminar, se inicia la misma secuencia de eventos que con GridView: un postback; seguido de que DetailsView rellene el DeleteParameters de su ObjectDataSource basándose en los valores del DataKeyNames; y completado con una llamada al método Delete() de su ObjectDataSource, que elimina realmente el producto de la base de datos. La edición en DetailsView también funciona de forma idéntica a la de GridView.

Para insertar, se presenta al usuario final un botón Nuevo que, cuando se hace clic, representa DetailsView en "modo de inserción". Con el "modo de inserción", el botón Nuevo se sustituye por los botones Insertar y Cancelar y solo se muestran los BoundFields cuya propiedad InsertVisible esté ajustada a true (el valor predeterminado). Aquellos campos de datos identificados como campos de autoincremento, como ProductID, tienen su propiedad InsertVisible establecida en false cuando se vincula el DetailsView al origen de datos a través de la etiqueta inteligente.

Al vincular un origen de datos a un DetailsView mediante la etiqueta inteligente, Visual Studio establece la propiedad InsertVisible en false solo para los campos de autoincremento. Los campos de solo lectura, como CategoryName y SupplierName, se mostrarán en la interfaz de usuario del "modo de inserción" a menos que su propiedad InsertVisible se establezca explícitamente en false. Tómese un momento para establecer las propiedades InsertVisible de estos dos campos en false, ya sea a través de la sintaxis declarativa de DetailsView o a través del enlace Editar campos de la etiqueta inteligente. La figura 19 muestra el ajuste de las propiedades InsertVisible a false haciendo clic en el enlace Editar campos.

Screenshot showing the Fields window with the InsertVisible property set to False.

Figura 19: Northwind Traders ofrece ahora té Acme (Haga clic para ver la imagen a tamaño completo)

Tras configurar las propiedades InsertVisible, visualice la página Basics.aspx en un navegador y haga clic en el botón Nuevo. La figura 20 muestra la VistaDetalles al añadir una nueva bebida, Té Acme, a nuestra línea de productos.

Screenshot showing the DetailsView of the Basics.aspx page in a web browser.

Figura 20: Northwind Traders ofrece ahora té Acme (Haga clic para ver la imagen a tamaño completo)

Tras introducir los datos de Té Acme y hacer clic en el botón Insertar, se produce un postback y el nuevo registro se añade a la tabla de la base de datos Products. Dado que en esta vista de detalles se enumeran los productos en orden con los que existen en la tabla de base de datos, debemos paginar al último producto para ver el nuevo producto.

Details for Acme Tea

Figura 21: Detalles para Té Acme (Haga clic para ver la imagen a tamaño completo)

Nota:

La propiedad CurrentMode de DetailsView indica la interfaz que se está mostrando y puede ser uno de los siguientes valores: Edit, Insert o ReadOnly. La propiedad DefaultMode indica el modo al que vuelve el DetailsView una vez finalizada una edición o inserción y es útil para mostrar un DetailsView que está permanentemente en modo edición o inserción.

Las funciones de inserción y edición mediante apuntar y hacer clic de la vista DetailsView presentan las mismas limitaciones que la vista GridView: el usuario debe introducir los valores CategoryID y SupplierID existentes a través de un cuadro de texto; la interfaz carece de toda lógica de validación; todos los campos de producto que no admiten valores NULL o no tienen un valor predeterminado especificado a nivel de la base de datos deben incluirse en la interfaz de inserción, etc.

Las técnicas que examinaremos para ampliar y mejorar la interfaz de edición de GridView en artículos futuros también se pueden aplicar a las interfaces de edición e inserción del control DetailsView.

Usar FormView para una interfaz de usuario de modificación de datos más flexible

FormView ofrece compatibilidad integrada para insertar, editar y eliminar datos, pero dado que usa plantillas en lugar de campos, no hay ningún lugar para agregar BoundFields o CommandField usados por los controles GridView y DetailsView para proporcionar la interfaz de modificación de datos. En su lugar, esta interfaz de los controles web para recopilar entradas de usuario al agregar un nuevo elemento o editar uno existente junto con los botones Nuevo, Editar, Eliminar, Insertar, Actualizar y Cancelar se deben agregar manualmente a las plantillas adecuadas. Afortunadamente, Visual Studio creará automáticamente la interfaz necesaria al enlazar FormView a un origen de datos a través de la lista desplegable de su etiqueta inteligente.

Para ilustrar estas técnicas, comience añadiendo un FormView a la página Basics.aspx y, desde la etiqueta inteligente del FormView, enlácelo al ObjectDataSource ya creado. Esto generará un EditItemTemplate, InsertItemTemplate y ItemTemplate para el FormView con controles web TextBox para recoger la entrada del usuario y controles web Botón para los botones Nuevo, Editar, Eliminar, Insertar, Actualizar y Cancelar. Además, la propiedad DataKeyNames del FormView se establece en el campo de clave principal (ProductID) del objeto devuelto por el ObjectDataSource. Por último, active la opción Habilitar paginación en la etiqueta inteligente de FormView.

A continuación se muestra el marcado declarativo para el ItemTemplate del FormView después de que este haya sido enlazado a ObjectDataSource. De forma predeterminada, cada campo de producto de valor no booleano se vincula a la propiedad Text de un control Web Label, mientras que cada campo de valor booleano (Discontinued) se vincula a la propiedad Checked de un control Web CheckBox desactivado. Para que los botones Nuevo, Editar y Eliminar activen determinados comportamientos de FormView al hacer clic en ellos, es imprescindible que sus valores CommandName se fijen en New, Edit y Delete, respectivamente.

<asp:FormView ID="FormView1" runat="server" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" AllowPaging="True">
    <EditItemTemplate>
        ...
    </EditItemTemplate>
    <InsertItemTemplate>
        ...
    </InsertItemTemplate>
    <ItemTemplate>
        ProductID:
        <asp:Label ID="ProductIDLabel" runat="server"
            Text='<%# Eval("ProductID") %>'></asp:Label><br />
        ProductName:
        <asp:Label ID="ProductNameLabel" runat="server"
            Text='<%# Bind("ProductName") %>'>
        </asp:Label><br />
        SupplierID:
        <asp:Label ID="SupplierIDLabel" runat="server"
            Text='<%# Bind("SupplierID") %>'>
        </asp:Label><br />
        CategoryID:
        <asp:Label ID="CategoryIDLabel" runat="server"
            Text='<%# Bind("CategoryID") %>'>
        </asp:Label><br />
        QuantityPerUnit:
        <asp:Label ID="QuantityPerUnitLabel" runat="server"
            Text='<%# Bind("QuantityPerUnit") %>'>
        </asp:Label><br />
        UnitPrice:
        <asp:Label ID="UnitPriceLabel" runat="server"
            Text='<%# Bind("UnitPrice") %>'></asp:Label><br />
        UnitsInStock:
        <asp:Label ID="UnitsInStockLabel" runat="server"
            Text='<%# Bind("UnitsInStock") %>'>
        </asp:Label><br />
        UnitsOnOrder:
        <asp:Label ID="UnitsOnOrderLabel" runat="server"
            Text='<%# Bind("UnitsOnOrder") %>'>
        </asp:Label><br />
        ReorderLevel:
        <asp:Label ID="ReorderLevelLabel" runat="server"
            Text='<%# Bind("ReorderLevel") %>'>
        </asp:Label><br />
        Discontinued:
        <asp:CheckBox ID="DiscontinuedCheckBox" runat="server"
            Checked='<%# Bind("Discontinued") %>'
            Enabled="false" /><br />
        CategoryName:
        <asp:Label ID="CategoryNameLabel" runat="server"
            Text='<%# Bind("CategoryName") %>'>
        </asp:Label><br />
        SupplierName:
        <asp:Label ID="SupplierNameLabel" runat="server"
            Text='<%# Bind("SupplierName") %>'>
        </asp:Label><br />
        <asp:LinkButton ID="EditButton" runat="server"
            CausesValidation="False" CommandName="Edit"
            Text="Edit">
        </asp:LinkButton>
        <asp:LinkButton ID="DeleteButton" runat="server"
            CausesValidation="False" CommandName="Delete"
            Text="Delete">
        </asp:LinkButton>
        <asp:LinkButton ID="NewButton" runat="server"
            CausesValidation="False" CommandName="New"
            Text="New">
        </asp:LinkButton>
    </ItemTemplate>
</asp:FormView>

La figura 22 muestra el ItemTemplate de FormView visto a través de un explorador. Cada campo de producto aparece con los botones Nuevo, Editar y Eliminar de la parte inferior.

The Defaut FormView ItemTemplate Lists Each Product Field Along with New, Edit, and Delete Buttons

Figura 22: El formulario Defaut FormViewItemTemplate Enumera cada campo del producto junto con los botones Nuevo, Editar y Eliminar (Haga clic para ver la imagen a tamaño completo)

Al igual que con GridView y DetailsView, al hacer clic en el botón Eliminar o en cualquier Botón, LinkButton o ImageButton cuya propiedad CommandName esté establecida como Eliminar se produce un postback, se rellena el DeleteParameters de ObjectDataSource basándose en el valor DataKeyNames de FormView y se invoca el método Delete() de ObjectDataSource.

Cuando se pulsa el botón Editar se produce un postback y los datos vuelven al EditItemTemplate, que se encarga de renderizar la interfaz de edición. Esta interfaz incluye los controles web para editar datos junto con los botones Actualizar y Cancelar. El EditItemTemplate generado predeterminadamente por Visual Studio contiene una Etiqueta para cualquier campo de autoincremento (ProductID), un TextBox para cada campo de valor no booleano y un CheckBox para cada campo de valor booleano. Este comportamiento es muy similar al de los BoundFields autogenerados en los controles GridView y DetailsView.

Nota:

Un pequeño problema con la autogeneración de EditItemTemplate de FormView es que genera controles web TextBox para aquellos campos que son de solo lectura, como CategoryName y SupplierName. Veremos cómo tener en cuenta esto en breve.

Los controles TextBox del EditItemTemplate tienen su propiedad Text vinculada al valor de su campo de datos correspondiente utilizando databinding bidireccional. El enlace de datos bidireccional, indicado por <%# Bind("dataField") %>, realiza el enlace de datos al enlazar datos a la plantilla y al rellenar los parámetros de ObjectDataSource para insertar o editar registros. Es decir, cuando el usuario pulsa el botón Editar del ItemTemplate, el método Bind() devuelve el valor del campo de datos especificado. Después de que el usuario realice sus cambios y haga clic en Actualizar, los valores devueltos que corresponden a los campos de datos especificados mediante Bind() se aplican al UpdateParameters de ObjectDataSource. Alternativamente, la vinculación de datos unidireccional, denotada por <%# Eval("dataField") %>, solo recupera los valores de los campos de datos cuando vincula datos a la plantilla y no devuelve los valores introducidos por el usuario a los parámetros de la fuente de datos en el postback.

El siguiente marcado declarativo muestra el EditItemTemplate de FormView. Observe que aquí se utiliza el método Bind() en la sintaxis de vinculación de datos y que los controles web de los botones Actualizar y Cancelar tienen sus propiedades CommandName configuradas en consecuencia.

<asp:FormView ID="FormView1" runat="server" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" AllowPaging="True">
    <EditItemTemplate>
        ProductID:
        <asp:Label ID="ProductIDLabel1" runat="server"
          Text="<%# Eval("ProductID") %>"></asp:Label><br />
        ProductName:
        <asp:TextBox ID="ProductNameTextBox" runat="server"
          Text="<%# Bind("ProductName") %>">
        </asp:TextBox><br />
        SupplierID:
        <asp:TextBox ID="SupplierIDTextBox" runat="server"
          Text="<%# Bind("SupplierID") %>">
        </asp:TextBox><br />
        CategoryID:
        <asp:TextBox ID="CategoryIDTextBox" runat="server"
          Text="<%# Bind("CategoryID") %>">
        </asp:TextBox><br />
        QuantityPerUnit:
        <asp:TextBox ID="QuantityPerUnitTextBox" runat="server"
           Text="<%# Bind("QuantityPerUnit") %>">
        </asp:TextBox><br />
        UnitPrice:
        <asp:TextBox ID="UnitPriceTextBox" runat="server"
           Text="<%# Bind("UnitPrice") %>">
        </asp:TextBox><br />
        UnitsInStock:
        <asp:TextBox ID="UnitsInStockTextBox" runat="server"
           Text="<%# Bind("UnitsInStock") %>">
        </asp:TextBox><br />
        UnitsOnOrder:
        <asp:TextBox ID="UnitsOnOrderTextBox" runat="server"
           Text="<%# Bind("UnitsOnOrder") %>">
        </asp:TextBox><br />
        ReorderLevel:
        <asp:TextBox ID="ReorderLevelTextBox" runat="server"
           Text="<%# Bind("ReorderLevel") %>">
        </asp:TextBox><br />
        Discontinued:
        <asp:CheckBox ID="DiscontinuedCheckBox" runat="server"
            Checked="<%# Bind("Discontinued") %>" /><br />
        CategoryName:
        <asp:TextBox ID="CategoryNameTextBox" runat="server"
             Text="<%# Bind("CategoryName") %>">
        </asp:TextBox><br />
        SupplierName:
        <asp:TextBox ID="SupplierNameTextBox" runat="server"
             Text="<%# Bind("SupplierName") %>">
        </asp:TextBox><br />
        <asp:LinkButton ID="UpdateButton" runat="server"
            CausesValidation="True" CommandName="Update"
            Text="Update">
        </asp:LinkButton>
        <asp:LinkButton ID="UpdateCancelButton" runat="server"
            CausesValidation="False" CommandName="Cancel"
            Text="Cancel">
        </asp:LinkButton>
    </EditItemTemplate>
    <InsertItemTemplate>
        ...
    </InsertItemTemplate>
    <ItemTemplate>
        ...
    </ItemTemplate>
</asp:FormView>

Nuestro EditItemTemplate, en este punto, provocará que se lance una excepción si intentamos usarlo. El problema es que los campos CategoryName y SupplierName se renderizan como controles Web TextBox en EditItemTemplate. Tenemos que cambiar estos TextBoxes por Etiquetas o eliminarlos por completo. Simplemente elimínelos por completo de EditItemTemplate.

En la figura 23 se muestra FormView en un explorador después de hacer clic en el botón Editar para Chai. Observe que los campos SupplierName y CategoryName que aparecen en la ItemTemplate ya no están presentes, pues acabamos de eliminarlos de EditItemTemplate. Cuando se hace clic en el botón Actualizar, FormView continúa con la misma secuencia de pasos que los controles GridView y DetailsView.

By Default the EditItemTemplate Shows Each Editable Product Field as a TextBox or CheckBox

Figura 23: De forma predeterminada EditItemTemplate muestra cada campo editable del producto como un cuadro de texto o una casilla de verificación (Haga clic para ver la imagen a tamaño completo)

Cuando se hace clic en el botón Insertar, se produce el postback de ItemTemplate de FormView. Sin embargo, no hay datos enlazados a FormView porque se agrega un nuevo registro. La interfaz InsertItemTemplate incluye los controles web para agregar un nuevo registro junto con los botones Insertar y Cancelar. El InsertItemTemplate generado de forma predeterminada por Visual Studio contiene un TextBox para cada campo de valor no booleano y un CheckBox para cada campo de valor booleano, de forma similar a la interfaz del EditItemTemplate autogenerado. Los controles TextBox tienen su propiedad Text vinculada al valor de su campo de datos correspondiente utilizando databinding bidireccional.

El siguiente marcado declarativo muestra el InsertItemTemplate de FormView. Observe que aquí se utiliza el método Bind() en la sintaxis de vinculación de datos y que los controles web de los botones Insertar y Cancelar tienen sus propiedades CommandName configuradas en consecuencia.

<asp:FormView ID="FormView1" runat="server" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" AllowPaging="True">
    <EditItemTemplate>
        ...
    </EditItemTemplate>
    <InsertItemTemplate>
        ProductName:
        <asp:TextBox ID="ProductNameTextBox" runat="server"
           Text="<%# Bind("ProductName") %>">
        </asp:TextBox><br />
        SupplierID:
        <asp:TextBox ID="SupplierIDTextBox" runat="server"
           Text="<%# Bind("SupplierID") %>">
        </asp:TextBox><br />
        CategoryID:
        <asp:TextBox ID="CategoryIDTextBox" runat="server"
           Text="<%# Bind("CategoryID") %>">
        </asp:TextBox><br />
        QuantityPerUnit:
        <asp:TextBox ID="QuantityPerUnitTextBox" runat="server"
           Text="<%# Bind("QuantityPerUnit") %>">
        </asp:TextBox><br />
        UnitPrice:
        <asp:TextBox ID="UnitPriceTextBox" runat="server"
           Text="<%# Bind("UnitPrice") %>">
        </asp:TextBox><br />
        UnitsInStock:
        <asp:TextBox ID="UnitsInStockTextBox" runat="server"
           Text="<%# Bind("UnitsInStock") %>">
        </asp:TextBox><br />
        UnitsOnOrder:
        <asp:TextBox ID="UnitsOnOrderTextBox" runat="server"
           Text="<%# Bind("UnitsOnOrder") %>">
        </asp:TextBox><br />
        ReorderLevel:
        <asp:TextBox ID="ReorderLevelTextBox" runat="server"
           Text="<%# Bind("ReorderLevel") %>">
        </asp:TextBox><br />
        Discontinued:
        <asp:CheckBox ID="DiscontinuedCheckBox" runat="server"
           Checked="<%# Bind("Discontinued") %>" /><br />
        CategoryName:
        <asp:TextBox ID="CategoryNameTextBox" runat="server"
            Text="<%# Bind("CategoryName") %>">
        </asp:TextBox><br />
        SupplierName:
        <asp:TextBox ID="SupplierNameTextBox" runat="server"
           Text="<%# Bind("SupplierName") %>">
        </asp:TextBox><br />
        <asp:LinkButton ID="InsertButton" runat="server"
            CausesValidation="True" CommandName="Insert"
            Text="Insert">
        </asp:LinkButton>
        <asp:LinkButton ID="InsertCancelButton" runat="server"
            CausesValidation="False" CommandName="Cancel"
            Text="Cancel">
        </asp:LinkButton>
    </InsertItemTemplate>
    <ItemTemplate>
        ...
    </ItemTemplate>
</asp:FormView>

Hay una sutileza con la generación automática de InsertItemTemplate de FormView. En concreto, los controles web TextBox se crean incluso para aquellos campos que son de solo lectura, como CategoryName y SupplierName. Al igual que con EditItemTemplate, es necesario quitar estos cuadros de texto de InsertItemTemplate.

En la figura 24 se muestra FormView en un explorador al agregar un nuevo producto, Café Acme. Observe que los campos SupplierName y CategoryName que aparecen en ItemTemplate ya no están presentes, pues acabamos de eliminarlos. Cuando se hace clic en el botón Insertar, FormView continúa con la misma secuencia de pasos que el control DetailsView, agregando un nuevo registro a la tabla Products. En la figura 25 se muestran los detalles del producto Café Acme en FormView después de insertarlo.

The InsertItemTemplate Dictates the FormView's Inserting Interface

Figura 24: El InsertItemTemplate dicta la interfaz de inserción de FormView (Haga clic para ver la imagen a tamaño completo)

The Details for New Product, Acme Coffee, are Displayed in the FormView

Figura 25: Los detalles del nuevo producto, Café Acme, se muestran en FormView (Haga clic para ver la imagen a tamaño completo)

Al separar las interfaces de solo lectura, edición e inserción en tres plantillas independientes, FormView permite un mayor grado de control sobre estas interfaces que DetailsView y GridView.

Nota:

Al igual que DetailsView, la propiedad CurrentMode de FormView indica la interfaz que se está mostrando y su propiedad DefaultMode indica el modo al que vuelve FormView tras finalizar una edición o inserción.

Resumen

En este tutorial hemos examinado los aspectos básicos de la inserción, edición y eliminación de datos mediante GridView, DetailsView y FormView. Los tres controles proporcionan cierto nivel de funcionalidades de modificación de datos integradas que se pueden usar sin escribir una sola línea de código en la página de ASP.NET gracias a los controles web de datos y ObjectDataSource. Sin embargo, las sencillas técnicas de apuntar y hacer clic dan lugar a una interfaz de usuario de modificación de datos bastante frágil e ingenua. Para proporcionar validación, inyectar valores programáticos, manejar con elegancia las excepciones, personalizar la interfaz de usuario, etc., tendremos que recurrir a un montón de técnicas que se discutirán en los próximos tutoriales.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él vía mitchell@4GuysFromRolla.com. o a través de su blog, que se puede encontrar en http://ScottOnWriting.NET.