Insertar un nuevo registro desde el pie de página de GridView (VB)
por Scott Mitchell
Aunque el control GridView no proporciona compatibilidad integrada para insertar un nuevo registro de datos, en este tutorial se muestra cómo mejorar GridView para incluir una interfaz de inserción.
Introducción
Como se describe en el tutorial Información general sobre la inserción, actualización y eliminación de datos, los controles web GridView, DetailsView y FormView incluyen funcionalidades de modificación de datos. Cuando se usa con controles de origen de datos declarativos, estos tres controles web se pueden configurar de forma rápida y sencilla para modificar datos y en escenarios sin necesidad de escribir una sola línea de código. Por desgracia, solo los controles DetailsView y FormView proporcionan funcionalidades integradas de inserción, edición y eliminación. GridView solo ofrece compatibilidad con la edición y la eliminación. Pero con un poco de esfuerzo, se puede mejorar el control GridView para incluir una interfaz de inserción.
Al agregar capacidades de inserción a GridView, debe decidir cómo se van a agregar los nuevos registros, crear la interfaz de inserción y escribir el código para insertar el nuevo registro. En este tutorial verá cómo agregar la interfaz de inserción a una fila de pie de página de GridView (vea la figura 1). La celda de pie de página de cada columna incluye el elemento de interfaz de usuario de colección de datos adecuado (un control TextBox si es un producto, un control DropDownList si es un proveedor, etc.). También se necesita una columna para un botón Agregar que, cuando se presione, provoque un postback e inserte un nuevo registro en la tabla Products
con los valores proporcionados en la fila de pie de página.
Figura 1: La fila de pie de página proporciona una interfaz para agregar nuevos productos (Haga clic para ver la imagen a tamaño completo)
Paso 1: Representación de información del producto en un control GridView
Antes de preocuparse por crear la interfaz de inserción en el pie de página del control GridView, se centrará en agregar un control GridView a la página donde se muestran los productos de la base de datos. Para empezar, abra la página InsertThroughFooter.aspx
en la carpeta EnhancedGridView
, arrastre un control GridView desde el cuadro de herramientas al Diseñador y establezca la propiedad ID
de GridView enProducts
. Luego, en la etiqueta inteligente del control GridView, elija enlazarlo a un nuevo ObjectDataSource denominado ProductsDataSource
.
Figura 2: Creación de una instancia de ObjectDataSource con el nombre ProductsDataSource
(Haga clic para ver la imagen a tamaño completo)
Configure ObjectDataSource para que use el método GetProducts()
de la clase ProductsBLL
para recuperar información de producto. En este tutorial, se centrará exclusivamente en agregar funcionalidades de inserción; no se preocupará por la edición ni la eliminación. Por tanto, asegúrese de que la lista desplegable de la pestaña INSERT está establecida en AddProduct()
y de que las listas desplegables de las pestañas UPDATE y DELETE están establecidas en (None).
Figura 3: Asignación del método AddProduct
al método Insert()
de ObjectDataSource (Haga clic para ver la imagen a tamaño completo)
Figura 4: Establecimiento de las pestañas UPDATE y DELETE de las listas desplegables en (None) (Haga clic para ver la imagen a tamaño completo)
Después de completar el asistente para la configuración de orígenes de datos del ObjectDataSource, Visual Studio agregará automáticamente campos al control GridView por cada uno de los campos de datos correspondientes. Por ahora, deje todos los campos que Visual Studio ha agregado. Más adelante en este tutorial, quitará algunos de los campos cuyos valores no es necesario especificar al agregar un nuevo registro.
Como hay cerca de 80 productos en la base de datos, un usuario tendrá que desplazarse hasta la parte inferior de la página web para agregar un nuevo registro. Por tanto, se habilitará la paginación para que la interfaz de inserción sea más visible y accesible. Para activar la paginación, simplemente active la casilla Habilitar paginación en la etiqueta inteligente del control GridView.
Llegado este punto, el marcado declarativo del control GridView y de ObjectDataSource debe ser similar al siguiente:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" EnableViewState="False">
<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>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
InsertMethod="AddProduct" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
<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>
Figura 5: Todos los campos de datos de producto se muestran en un control GridView con paginación (Haga clic para ver la imagen a tamaño completo)
Paso 2: Adición de una fila de pie de página
Además de filas de encabezado y de datos, GridView incluye una fila de pie de página. Las filas de encabezado y de pie de página se muestran en función de los valores de las propiedades ShowHeader
y ShowFooter
del control GridView. Para mostrar la fila de pie de página, simplemente establezca la propiedad ShowFooter
en True
. Como se muestra en la figura 6, al establecer la propiedad ShowFooter
en True
, se agrega una fila de pie de página a la tabla.
Figura 6: Para mostrar la fila de pie de página, establezca ShowFooter
en True
(Haga clic para ver la imagen a tamaño completo)
Observe que la fila de pie de página tiene un color de fondo rojo oscuro. Esto se debe al tema DataWebControls que se ha creado y aplicado a todas las páginas en el tutorial Representación de datos con ObjectDataSource. En concreto, el archivo GridView.skin
configura la propiedad FooterStyle
de forma que usa la clase FooterStyle
de CSS. La clase FooterStyle
se define en Styles.css
del modo siguiente:
.FooterStyle
{
background-color: #a33;
color: White;
text-align: right;
}
Nota:
En tutoriales anteriores, se ha analizado el uso de la fila de pie de página de GridView. Si es necesario, consulte el tutorial Representación de información de resumen en el pie de página de GridView a modo de repaso.
Después de establecer la propiedad ShowFooter
en True
, dedique un momento a ver la salida en un explorador. Actualmente, la fila de pie de página no contiene ningún texto ni controles web. En el paso 3, modificará el pie de página de cada campo del control GridView para que incluya la interfaz de inserción adecuada.
Figura 7: La fila de pie de página vacía se muestra encima de los controles de la interfaz de paginación (Haga clic para ver la imagen a tamaño completo)
Paso 3: Personalización de la fila de pie de página
En el tutorial Uso de controles TemplateField en el control GridView ha visto cómo personalizar profundamente la visualización de una determinada columna de GridView mediante controles TemplateField (en lugar de BoundField o CheckBoxField); en Personalización de la interfaz de modificación de datos, se ha examinado el uso de controles TemplateField para personalizar la interfaz de edición en un control GridView. Recordará que un control TemplateField se compone de una serie de plantillas que definen la combinación de marcado, controles web y sintaxis de enlace de datos usada para determinados tipos de filas. Por ejemplo, ItemTemplate
especifica la plantilla usada para las filas de solo lectura, mientras que EditItemTemplate
define la plantilla usada para la fila editable.
Junto con ItemTemplate
y EditItemTemplate
, el control TemplateField incluye también un objeto FooterTemplate
que especifica el contenido de la fila de pie de página. Por tanto, se pueden agregar a FooterTemplate
los controles web necesarios para la interfaz de inserción de cada campo. Para empezar, convierta todos los campos del control GridView en controles TemplateField. Para ello, haga clic en el vínculo Editar columnas de la etiqueta inteligente del control GridView, seleccione cada campo de la esquina inferior izquierda y haga clic en el vínculo Convertir este campo en un TemplateField.
Figura 8: Conversión de cada campo en un control TemplateField
Al hacer clic en Convertir este campo en un TemplateField, el tipo de campo actual se convierte en un control TemplateField equivalente. Por ejemplo, cada control BoundField se reemplazará por un control TemplateField con un elemento ItemTemplate
que contiene una etiqueta donde se muestra el campo de datos correspondiente y un elemento EditItemTemplate
que muestra el campo de datos en un control TextBox. El control BoundField ProductName
se ha convertido en el siguiente marcado de TemplateField:
<asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Bind("ProductName") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
Del mismo modo, el control CheckBoxField Discontinued
se ha convertido en un control TemplateField cuyos elementos ItemTemplate
y EditItemTemplate
contienen un control web CheckBox (con el valor ItemTemplate
de CheckBox deshabilitado). El control BoundField ProductID
de solo lectura se ha convertido en un control TemplateField con un control Label en ItemTemplate
y EditItemTemplate
. En resumen, convertir un campo de GridView existente en un control TemplateField es una manera rápida y fácil de cambiar al uso de TemplateField, que son más personalizables, sin perder ninguna de las funciones de campo existentes.
Como el control GridView con el que trabaja no admite la edición, no dude en quitar EditItemTemplate
de cada control TemplateField y dejar únicamente ItemTemplate
. Tras ello, el marcado declarativo de GridView debe ser similar al siguiente:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" EnableViewState="False" ShowFooter="True">
<Columns>
<asp:TemplateField HeaderText="ProductID" InsertVisible="False"
SortExpression="ProductID">
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("ProductID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="SupplierID" SortExpression="SupplierID">
<ItemTemplate>
<asp:Label ID="Label3" runat="server"
Text='<%# Bind("SupplierID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="CategoryID" SortExpression="CategoryID">
<ItemTemplate>
<asp:Label ID="Label4" runat="server"
Text='<%# Bind("CategoryID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="QuantityPerUnit"
SortExpression="QuantityPerUnit">
<ItemTemplate>
<asp:Label ID="Label5" runat="server"
Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="UnitPrice" SortExpression="UnitPrice">
<ItemTemplate>
<asp:Label ID="Label6" runat="server"
Text='<%# Bind("UnitPrice") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="UnitsInStock"
SortExpression="UnitsInStock">
<ItemTemplate>
<asp:Label ID="Label7" runat="server"
Text='<%# Bind("UnitsInStock") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="UnitsOnOrder"
SortExpression="UnitsOnOrder">
<ItemTemplate>
<asp:Label ID="Label8" runat="server"
Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="ReorderLevel"
SortExpression="ReorderLevel">
<ItemTemplate>
<asp:Label ID="Label9" runat="server"
Text='<%# Bind("ReorderLevel") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Discontinued"
SortExpression="Discontinued">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server"
Checked='<%# Bind("Discontinued") %>' Enabled="false" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="CategoryName"
SortExpression="CategoryName">
<ItemTemplate>
<asp:Label ID="Label10" runat="server"
Text='<%# Bind("CategoryName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="SupplierName"
SortExpression="SupplierName">
<ItemTemplate>
<asp:Label ID="Label11" runat="server"
Text='<%# Bind("SupplierName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Ahora que cada campo de GridView se ha convertido en un control TemplateField, se puede escribir la interfaz de inserción adecuada en el valor FooterTemplate
de cada campo. Algunos campos no tendrán una interfaz de inserción (ProductID
, por ejemplo); otros variarán en los controles web usados para recopilar la nueva información de producto.
Para crear la interfaz de edición, seleccione el vínculo Editar plantillas de la etiqueta inteligente del control GridView. Después, en la lista desplegable, seleccione el valor FooterTemplate
del campo adecuado y arrastre el control adecuado desde el cuadro de herramientas al Diseñador.
Figura 9: Adición de la interfaz de inserción adecuada al valor FooterTemplate
de cada campo (Haga clic para ver la imagen a tamaño completo)
En la siguiente lista con viñetas se enumeran los campos de GridView y se especifica la interfaz de inserción que se va a agregar:
ProductID
: ninguna.ProductName
: agregue un control TextBox y establezca su valorID
enNewProductName
. Agregue también un control RequiredFieldValidator para asegurarse de que el usuario escribe un valor de nombre del nuevo producto.SupplierID
: ninguna.CategoryID
: ninguna.QuantityPerUnit
: agregue un control TextBox y establezca su valorID
enNewQuantityPerUnit
.UnitPrice
: agregue un control TextBox denominadoNewUnitPrice
y un control CompareValidator que garantice que el valor especificado sea un valor de moneda mayor o igual que cero.UnitsInStock
: use un control TextBox cuyo valorID
esté establecido enNewUnitsInStock
. Incluya un elemento CompareValidator que garantice que el valor especificado sea un valor entero mayor o igual que cero.UnitsOnOrder
: use un control TextBox cuyo valorID
esté establecido enNewUnitsOnOrder
. Incluya un elemento CompareValidator que garantice que el valor especificado sea un valor entero mayor o igual que cero.ReorderLevel
: use un control TextBox cuyo valorID
esté establecido enNewReorderLevel
. Incluya un elemento CompareValidator que garantice que el valor especificado sea un valor entero mayor o igual que cero.Discontinued
: agregue un control CheckBox y establezca su valorID
enNewDiscontinued
.CategoryName
: agregue un control DropDownList y establezca su valorID
enNewCategoryID
. Enlácelo a una nueva instancia de ObjectDataSource con el nombreCategoriesDataSource
y configúrelo para usar el métodoCategoriesBLL
de la claseGetCategories()
. Haga que los elementosListItem
del control DropDownList muestren el campo de datosCategoryName
, utilizando para ello el campo de datosCategoryID
como sus valores.SupplierName
: agregue un control DropDownList y establezca su valorID
enNewSupplierID
. Enlácelo a una nueva instancia de ObjectDataSource con el nombreSuppliersDataSource
y configúrelo para usar el métodoSuppliersBLL
de la claseGetSuppliers()
. Haga que los elementosListItem
del control DropDownList muestren el campo de datosCompanyName
, utilizando para ello el campo de datosSupplierID
como sus valores.
En cada uno de los controles de validación, borre la propiedad ForeColor
para que se use el color de primer plano blanco de la clase FooterStyle
de CSS en lugar del rojo predeterminado. Use también la propiedad ErrorMessage
para obtener una descripción detallada, pero establezca la propiedad Text
en un asterisco. Para evitar que el texto del control de validación haga que la interfaz de inserción se ajuste en dos líneas, establezca la propiedad Wrap
de FooterStyle
en false en cada uno de los objetos FooterTemplate
que usen un control de validación. Por último, agregue un control ValidationSummary debajo de GridView y establezca su propiedad ShowMessageBox
en True
y su propiedad ShowSummary
en False
.
Al agregar un nuevo producto, se deben proporcionar los valores CategoryID
y SupplierID
. Esta información se captura mediante los controles DropDownList en las celdas de pie de página de los campos CategoryName
y SupplierName
. Aquí se ha optado por usar estos campos en lugar de los controles TemplateField CategoryID
y SupplierID
porque en las filas de datos de la tabla probablemente el usuario esté más interesado en ver los nombres de categoría y proveedor que sus identificadores. Como ahora los valores de CategoryID
y SupplierID
se capturan en las interfaces de inserción de los campos CategoryName
y SupplierName
, se pueden quitar los controles TemplateField CategoryID
y SupplierID
del control GridView.
Del mismo modo, ProductID
no se usa al agregar un nuevo producto, por lo que el control TemplateField ProductID
también se puede quitar. Pero se dejará el campo ProductID
en la tabla. Además de los controles TextBox, DropDownList, CheckBox y de validación que componen la interfaz de inserción, también se necesitará un botón Agregar que, cuando se presione, ponga en marcha la lógica para agregar el nuevo producto a la base de datos. En el paso 4 se incluirá un botón Agregar en la interfaz de inserción, en el elemento FooterTemplate
del control TemplateField ProductID
.
No dude en mejorar la apariencia de los distintos campos del control GridView. Por ejemplo, es posible que quiera dar un formato de moneda a los valores UnitPrice
, alinear a la derecha los campos UnitsInStock
, UnitsOnOrder
y ReorderLevel
, y actualizar los valores HeaderText
de los controles TemplateField.
Después de crear las interfaces de inserción en FooterTemplate
, de quitar los controles TemplateField SupplierID
y CategoryID
, y de mejorar la estética de la tabla aplicando formato y alineando los controles TemplateField, el marcado declarativo del control GridView debe ser similar al siguiente:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" EnableViewState="False" ShowFooter="True">
<Columns>
<asp:TemplateField HeaderText="ProductID" InsertVisible="False"
SortExpression="ProductID">
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("ProductID") %>'></asp:Label>
</ItemTemplate>
<ItemStyle HorizontalAlign="Center" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewProductName" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
runat="server" ControlToValidate="NewProductName"
Display="Dynamic" ForeColor="
ErrorMessage="You must enter a name for the new product.">
* </asp:RequiredFieldValidator>
</FooterTemplate>
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
<ItemTemplate>
<asp:Label ID="Label10" runat="server"
Text='<%# Bind("CategoryName") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:DropDownList ID="NewCategoryID" runat="server"
DataSourceID="CategoriesDataSource"
DataTextField="CategoryName" DataValueField="CategoryID">
</asp:DropDownList>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
<ItemTemplate>
<asp:Label ID="Label11" runat="server"
Text='<%# Bind("SupplierName") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:DropDownList ID="NewSupplierID" runat="server"
DataSourceID="SuppliersDataSource"
DataTextField="CompanyName" DataValueField="SupplierID">
</asp:DropDownList><asp:ObjectDataSource ID="SuppliersDataSource"
runat="server" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Qty/Unit" SortExpression="QuantityPerUnit">
<ItemTemplate>
<asp:Label ID="Label5" runat="server"
Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewQuantityPerUnit" runat="server"></asp:TextBox>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<ItemTemplate>
<asp:Label ID="Label6" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
$<asp:TextBox ID="NewUnitPrice" runat="server" Columns="8" />
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="NewUnitPrice"
ErrorMessage="You must enter a valid currency value greater than
or equal to 0.00. Do not include the currency symbol."
ForeColor="" Operator="GreaterThanEqual" Type="Currency"
ValueToCompare="0" Display="Dynamic">
* </asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Units In Stock"
SortExpression="Units In Stock">
<ItemTemplate>
<asp:Label ID="Label7" runat="server"
Text='<%# Bind("UnitsInStock") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewUnitsInStock" runat="server" Columns="5" />
<asp:CompareValidator ID="CompareValidator2" runat="server"
ControlToValidate="NewUnitsInStock" Display="Dynamic"
ErrorMessage="You must enter a valid numeric value for units
in stock that's greater than or equal to zero."
ForeColor="" Operator="GreaterThanEqual" Type="Integer"
ValueToCompare="0">*</asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Units On Order" SortExpression="UnitsOnOrder">
<ItemTemplate>
<asp:Label ID="Label8" runat="server"
Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewUnitsOnOrder" runat="server" Columns="5" />
<asp:CompareValidator ID="CompareValidator3" runat="server"
ControlToValidate="NewUnitsOnOrder" Display="Dynamic"
ErrorMessage="You must enter a valid numeric value for units on
order that's greater than or equal to zero."
ForeColor="" Operator="GreaterThanEqual" Type="Integer"
ValueToCompare="0">*</asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Reorder Level" SortExpression="ReorderLevel">
<ItemTemplate>
<asp:Label ID="Label9" runat="server"
Text='<%# Bind("ReorderLevel") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewReorderLevel" runat="server" Columns="5" />
<asp:CompareValidator ID="CompareValidator4" runat="server"
ControlToValidate="NewReorderLevel" Display="Dynamic"
ErrorMessage="You must enter a valid numeric value for reorder
level that's greater than or equal to zero."
ForeColor="" Operator="GreaterThanEqual" Type="Integer"
ValueToCompare="0">*</asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server"
Checked='<%# Bind("Discontinued") %>' Enabled="false" />
</ItemTemplate>
<FooterTemplate>
<asp:CheckBox ID="NewDiscontinued" runat="server" />
</FooterTemplate>
<ItemStyle HorizontalAlign="Center" />
<FooterStyle HorizontalAlign="Center" />
</asp:TemplateField>
</Columns>
</asp:GridView>
Cuando se visualiza en un explorador, la fila de pie de página del control GridView incluye ahora la interfaz de inserción completada (vea la figura 10). En este momento, la interfaz de inserción no incluye un medio para que el usuario indique que ha introducido los datos del nuevo producto y que quiere insertar un nuevo registro en la base de datos. Además, todavía no se ha abordado cómo se van a convertir los datos introducidos en el pie de página en un nuevo registro de la base de datos Products
. En el paso 4 verá cómo incluir un botón Agregar a la interfaz de inserción y cómo ejecutar código en un postback cuando se haga clic en él. En el paso 5 se describe cómo insertar un nuevo registro con los datos del pie de página.
Figura 10: El pie de página del control GridView proporciona una interfaz para agregar un nuevo registro (Haga clic para ver la imagen a tamaño completo)
Paso 4: Inclusión de un botón Agregar en la interfaz de inserción
Es necesario incluir un botón Agregar en algún lugar de la interfaz de inserción de la fila de pie de página, ya que carece actualmente de alguna forma de que el usuario indique que ha completado la entrada de la nueva información del producto. Este botón se podría colocar en uno de los elementos FooterTemplate
existentes, o bien se podría agregar una nueva columna a la tabla para este propósito. En este tutorial, el botón Agregar se colocará en el elemento FooterTemplate
del control TemplateField ProductID
.
En el Diseñador, haga clic en el vínculo Editar plantillas de la etiqueta inteligente del control GridView y, después, seleccione el elemento FooterTemplate
del campo ProductID
en la lista desplegable. Agregue a la plantilla un control web Button (o LinkButton o ImageButton, si lo prefiere), establezca su identificador en AddProduct
, CommandName
en Insertar y su propiedad Text
en Agregar, como se muestra en la figura 11.
Figura 11: Colocación del botón Agregar en el elemento FooterTemplate
del control TemplateField ProductID
(Haga clic para ver la imagen a tamaño completo)
Una vez que haya incluido el botón Agregar, pruebe la página en un explorador. Fíjese en que, al hacer clic en el botón Agregar con datos no válidos en la interfaz de inserción, el postback se cortocircuita y el control ValidationSummary indica los datos no válidos (vea la figura 12). Cuando se especifican los datos adecuados, al hacer clic en el botón Agregar, se produce un postback. Pero no se agrega ningún registro a la base de datos. Tendrá que escribir de código para que la inserción se realice de verdad.
Figura 12: El postback del botón Agregar se cortocircuita si hay datos no válidos en la interfaz de inserción (Haga clic para ver la imagen a tamaño completo)
Nota:
Los controles de validación de la interfaz de inserción no estaban asignados a un grupo de validación. Esto funciona bien siempre que la interfaz de inserción sea el único conjunto de controles de validación de la página. Pero si hay otros controles de validación en la página (como los controles de validación en la interfaz de edición de la cuadrícula), los controles de validación de la interfaz de inserción y las propiedades ValidationGroup
del botón Agregar deben tener asignado el mismo valor para asociar estos controles a un grupo de validación determinado. Vea Disección de los controles de validación de ASP.NET 2.0 para más información sobre cómo dividir en grupos de validación los controles de validación y los botones de una página.
Paso 5: Inserción de un nuevo registro en la tabla Products
Al usar las características de edición integradas del control GridView, este controla automáticamente todo el trabajo necesario para realizar la actualización. En concreto, cuando se hace clic en el botón Actualizar, copia los valores especificados desde la interfaz de edición a los parámetros de la colección UpdateParameters
de ObjectDataSource e inicia la actualización mediante la invocación del método Update()
de ObjectDataSource. Como el control GridView no proporciona una funcionalidad integrada de este tipo para realizar inserciones, se debe implementar código que llame al método Insert()
de ObjectDataSource y copie los valores de la interfaz de inserción en la colección InsertParameters
de ObjectDataSource.
Esta lógica de inserción debe ejecutarse después de hacer clic en el botón Agregar. Como se describe en el tutorial Adición y respuesta a los botones en un control GridView, cuando se hace clic en un control Button, LinkButton o ImageButton de un control GridView, se activa el evento RowCommand
del control GridView en el postback. Este evento se activa tanto si el control Button, LinkButton o ImageButton se ha agregado explícitamente (como el botón Agregar en la fila de pie de página), como si se ha agregado automáticamente mediante el control GridView (como los controles LinkButton de la parte superior de cada columna cuando se selecciona Habilitar ordenación, o los controles LinkButton de la interfaz de paginación cuando se selecciona Habilitar paginación).
Por tanto, para responder al usuario que hace clic en el botón Agregar, es necesario crear un controlador de eventos para el evento RowCommand
del control GridView. Como este evento se activa cada vez que se hace clic en cualquier control Button, LinkButton o ImageButton del control GridView, es fundamental continuar con la lógica de inserción solo si la propiedad CommandName
pasada al controlador de eventos se asigna al valor CommandName
del botón Agregar (Insertar). Además, solo se debería continuar si los controles de validación notifican datos válidos. Para dar cabida a esto, cree un controlador de eventos para el evento RowCommand
con el código siguiente:
Protected Sub Products_RowCommand(sender As Object, e As GridViewCommandEventArgs) _
Handles Products.RowCommand
' Insert data if the CommandName == "Insert"
' and the validation controls indicate valid data...
If e.CommandName = "Insert" AndAlso Page.IsValid Then
' TODO: Insert new record...
End If
End Sub
Nota:
Es posible que se pregunte por qué el controlador de eventos se molesta en comprobar la propiedad Page.IsValid
. Al fin y al cabo, ¿no se suprimirá el postback si se proporcionan datos no válidos en la interfaz de inserción? Esta suposición es correcta siempre que el usuario no haya deshabilitado JavaScript o haya tomado medidas para eludir la lógica de validación del lado cliente. En resumen, nunca se debe confiar estrictamente en la validación del lado cliente; siempre se debe realizar una comprobación de validez del lado servidor antes de trabajar con los datos.
En el paso 1 se ha creado el elemento ObjectDataSource ProductsDataSource
de forma que su método Insert()
se asigna al método AddProduct
de la clase ProductsBLL
. Para insertar el nuevo registro en la tabla Products
, simplemente se puede invocar el método Insert()
de ObjectDataSource:
Protected Sub Products_RowCommand(sender As Object, e As GridViewCommandEventArgs) _
Handles Products.RowCommand
' Insert data if the CommandName == "Insert"
' and the validation controls indicate valid data...
If e.CommandName = "Insert" AndAlso Page.IsValid Then
' Insert new record
ProductsDataSource.Insert()
End If
End Sub
Ahora que se ha invocado el método Insert()
, lo único que queda es copiar los valores de la interfaz de inserción en los parámetros pasados al método AddProduct
de la clase ProductsBLL
. Como ha visto en el tutorial Examen de los eventos relacionados con la inserción, actualización y eliminación, esto se puede lograr mediante el evento Inserting
de ObjectDataSource. En el evento Inserting
, es necesario hacer referencia mediante programación a los controles de la fila de pie de tabla del control GridView Products
y asignar sus valores a la colección e.InputParameters
. Si el usuario omite un valor, como dejar el control TextBox ReorderLevel
en blanco, es necesario especificar que el valor insertado en la base de datos debe ser NULL
. Como el método AddProducts
admite tipos que aceptan valores NULL en los campos de base de datos que aceptan valores NULL, simplemente use un tipo que acepte valores NULL y establezca su valor en Nothing
en caso de que la entrada del usuario se omita.
Protected Sub ProductsDataSource_Inserting _
(sender As Object, e As .ObjectDataSourceMethodEventArgs) _
Handles ProductsDataSource.Inserting
' Programmatically reference Web controls in the inserting interface...
Dim NewProductName As TextBox = _
Products.FooterRow.FindControl("NewProductName")
Dim NewCategoryID As DropDownList = _
Products.FooterRow.FindControl("NewCategoryID")
Dim NewSupplierID As DropDownList = _
Products.FooterRow.FindControl("NewSupplierID")
Dim NewQuantityPerUnit As TextBox = _
Products.FooterRow.FindControl("NewQuantityPerUnit")
Dim NewUnitPrice As TextBox = _
Products.FooterRow.FindControl("NewUnitPrice")
Dim NewUnitsInStock As TextBox = _
Products.FooterRow.FindControl("NewUnitsInStock")
Dim NewUnitsOnOrder As TextBox = _
Products.FooterRow.FindControl("NewUnitsOnOrder")
Dim NewReorderLevel As TextBox = _
Products.FooterRow.FindControl("NewReorderLevel")
Dim NewDiscontinued As CheckBox = _
Products.FooterRow.FindControl("NewDiscontinued")
' Set the ObjectDataSource's InsertParameters values...
e.InputParameters("productName") = NewProductName.Text
e.InputParameters("supplierID") = _
Convert.ToInt32(NewSupplierID.SelectedValue)
e.InputParameters("categoryID") = _
Convert.ToInt32(NewCategoryID.SelectedValue)
Dim quantityPerUnit As String = Nothing
If Not String.IsNullOrEmpty(NewQuantityPerUnit.Text) Then
quantityPerUnit = NewQuantityPerUnit.Text
End If
e.InputParameters("quantityPerUnit") = quantityPerUnit
Dim unitPrice As Nullable(Of Decimal) = Nothing
If Not String.IsNullOrEmpty(NewUnitPrice.Text) Then
unitPrice = Convert.ToDecimal(NewUnitPrice.Text)
End If
e.InputParameters("unitPrice") = unitPrice
Dim unitsInStock As Nullable(Of Short) = Nothing
If Not String.IsNullOrEmpty(NewUnitsInStock.Text) Then
unitsInStock = Convert.ToInt16(NewUnitsInStock.Text)
End If
e.InputParameters("unitsInStock") = unitsInStock
Dim unitsOnOrder As Nullable(Of Short) = Nothing
If Not String.IsNullOrEmpty(NewUnitsOnOrder.Text) Then
unitsOnOrder = Convert.ToInt16(NewUnitsOnOrder.Text)
End If
e.InputParameters("unitsOnOrder") = unitsOnOrder
Dim reorderLevel As Nullable(Of Short) = Nothing
If Not String.IsNullOrEmpty(NewReorderLevel.Text) Then
reorderLevel = Convert.ToInt16(NewReorderLevel.Text)
End If
e.InputParameters("reorderLevel") = reorderLevel
e.InputParameters("discontinued") = NewDiscontinued.Checked
End Sub
Una vez que se complete el controlador de eventos Inserting
, se pueden agregar nuevos registros a la tabla de base de datos Products
desde la fila de pie de página del control GridView. Pruebe a agregar varios productos nuevos.
Mejora y personalización de la operación de adición
Actualmente, al hacer clic en el botón Agregar, se agrega un nuevo registro a la tabla de base de datos, pero no se proporciona ningún tipo de pista visual que indique que el registro se ha agregado correctamente. Lo ideal es que un control web Label o un cuadro de alerta del lado cliente informe al usuario de que la inserción se ha completado correctamente. Lo dejo como ejercicio para el lector.
El control GridView usado en este tutorial no aplica ningún criterio de ordenación a los productos enumerados, ni permite al usuario final ordenar los datos. Por tanto, los registros se ordenan como están en la base de datos, por el campo de clave principal. Como cada nuevo registro tiene un valor ProductID
mayor que el último, cada vez que se agrega un nuevo producto, se coloca al final de la tabla. Por tanto, seguramente sea interesante dirigir al usuario de forma automática a la última página del control GridView después de agregar un nuevo registro. Esto se puede lograr si se agrega la siguiente línea de código después de la llamada a ProductsDataSource.Insert()
en el controlador de eventos RowCommand
para indicar que hay que dirigir al usuario a la última página después de enlazar los datos al control GridView:
' Indicate that the user needs to be sent to the last page
SendUserToLastPage = True
SendUserToLastPage
es una variable booleana de nivel de página que tiene asignado un valor inicial de False
. En el controlador de eventos DataBound
del control GridView, si SendUserToLastPage
es false, la propiedad PageIndex
se actualiza para dirigir al usuario a la última página.
Protected Sub Products_DataBound(sender As Object, e As EventArgs) _
Handles Products.DataBound
' Send user to last page of data, if needed
If SendUserToLastPage Then
Products.PageIndex = Products.PageCount - 1
End If
End Sub
La razón por la que la propiedad PageIndex
se establece en el controlador de eventos DataBound
(en lugar del controlador de eventos RowCommand
) es que cuando el controlador de eventos RowCommand
se activa, aún falta agregar el nuevo registro a la tabla de base de datos Products
. Por tanto, en el controlador de eventos RowCommand
, el índice de la última página (PageCount - 1
) representa el índice de la última página antes de agregar el nuevo producto. En la mayoría de los productos que se agregan, el índice de la última página es el mismo después de agregar el nuevo producto. Pero cuando el producto agregado da como resultado un nuevo índice de última página, si se actualiza incorrectamente el elemento PageIndex
en el controlador de eventos RowCommand
, se dirigirá a la antepenúltima página (el índice de última página anterior a la adición del nuevo producto) en lugar del nuevo índice de última página. Como el controlador de eventos DataBound
se activa después de agregar el nuevo producto y los datos se vuelven a enlazar a la tabla, al establecer la propiedad PageIndex
allí, sabrá que está en el índice de última página correcto.
Por último, el control GridView usado en este tutorial es bastante amplio debido al número de campos que se deben recopilar para agregar un nuevo producto. Debido a este ancho, es posible que se prefiera un diseño vertical del control DetailsView. El ancho general del control GridView podría reducirse recopilando menos entradas. Quizás no sea necesario recopilar los campos UnitsOnOrder
, UnitsInStock
y ReorderLevel
al agregar un nuevo producto, en cuyo caso estos campos se podrían quitar del control GridView.
Para ajustar los datos recopilados, puede usar uno de estos dos enfoques:
- Seguir usando el método
AddProduct
, que espera valores en los camposUnitsOnOrder
,UnitsInStock
yReorderLevel
. En el controlador de eventosInserting
, proporcione valores predeterminados codificados de forma rígida que se usarán para estas entradas que se han quitado de la interfaz de inserción. - Crear una sobrecarga del método
AddProduct
en la claseProductsBLL
que no acepte entradas en los camposUnitsOnOrder
,UnitsInStock
yReorderLevel
. Después, en la página ASP.NET, configure ObjectDataSource para usar esta nueva sobrecarga.
Ambas opciones funcionarán igual de bien. En los tutoriales anteriores se ha usado la última opción y se crearon varias sobrecargas para el método UpdateProduct
de la clase ProductsBLL
.
Resumen
El control GridView carece de las funcionalidades de inserción integradas que sí tienen los controles DetailsView y FormView, pero con un poco de esfuerzo se puede agregar una interfaz de inserción a la fila de pie de página. Para mostrar la fila de pie de página en el control GridView, simplemente establezca su propiedad ShowFooter
en True
. El contenido de la fila de pie de página se puede personalizar para cada campo; para ello, el campo se convierte en un control TemplateField y se agrega la interfaz de inserción a FooterTemplate
. Como ha visto en este tutorial, FooterTemplate
puede contener controles Button, TextBox, DropDownList y CheckBox, así como controles de origen de datos para rellenar controles web basados en datos (como DropDownList) y controles de validación. Junto con los controles para recopilar la entrada del usuario, es necesario agregar un control Button, LinkButton o ImageButton.
Cuando se hace clic en el botón Agregar, se invoca el método Insert()
de ObjectDataSource para iniciar el flujo de trabajo de inserción. Después, ObjectDataSource llamará al método de inserción configurado (en este tutorial, el método AddProduct
de la clase ProductsBLL
). Será necesario copiar los valores de la interfaz de inserción del control GridView en la colección InsertParameters
de ObjectDataSource antes de invocar el método de inserción. Esto se puede lograr si se hace referencia mediante programación a los controles web de la interfaz de inserción en el controlador de eventos Inserting
de ObjectDataSource.
Con este tutorial se completa el análisis de las técnicas existentes para mejorar la apariencia del control GridView. En el siguiente grupo de tutoriales se examinará cómo trabajar con datos binarios, como imágenes, PDF, documentos de Word, etc. y los controles web de datos.
¡Feliz programación!
Acerca del autor
Scott Mitchell, autor de siete libros de ASP y ASP.NET, y fundador de 4GuysFromRolla.com, trabaja 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 a través de mitchell@4GuysFromRolla.com. o de su blog, que se puede encontrar en http://ScottOnWriting.NET.
Agradecimientos especiales a
Esta serie de tutoriales fue revisada por muchos revisores. La revisora principal de este tutorial ha sido Bernadette Leigh. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.