Maestro y detalles mediante una lista con viñetas de registros maestros con un control DataList de detalles (VB)
por Scott Mitchell
En este tutorial se comprimirá el informe maestro/detalle de dos páginas del tutorial anterior en una sola página, en la que se muestra una lista con viñetas de nombres de categoría en el lado izquierdo de la pantalla y los productos de la categoría seleccionada en el derecho.
Introducción
En el tutorial anterior ha visto cómo dividir un informe maestro/detalle en dos páginas. En la página maestra se ha usado un control Repeater para representar una lista con viñetas de categorías. Cada nombre de categoría era un hipervínculo que, al hacer clic en él, llevaba al usuario a la página de detalles, donde un objeto DataList de dos columnas mostraba los productos pertenecientes a la categoría seleccionada.
En este tutorial se comprimirán las dos páginas en una sola, y se mostrará una lista con viñetas de nombres de categoría en el lado izquierdo de la pantalla, con cada nombre de categoría representado como un control LinkButton. Al hacer clic en uno de los controles LinkButton de nombre de categoría, se produce un postback y los productos de la categoría seleccionada se enlazan a un control DataList de dos columnas a la derecha de la pantalla. Además de mostrar cada nombre de categoría, el control Repeater de la izquierda muestra cuántos productos totales hay en una categoría determinada (vea la figura 1).
Figura 1: El nombre de la categoría y el número total de productos se muestran a la izquierda (Haga clic para ver la imagen a tamaño completo)
Paso 1: Representación de un control Repeater en la parte izquierda de la pantalla
En este tutorial es necesario que aparezca una lista con viñetas de categorías a la izquierda de los productos de la categoría seleccionada. El contenido de una página web se puede colocar mediante etiquetas de párrafo de elementos HTML estándar, espacios de no separación, <table>
, etc., o bien mediante técnicas de hoja de estilos en cascada (CSS). En todos los tutoriales hasta ahora se han usado técnicas de CSS para la colocación de elementos. Al crear la interfaz de usuario de navegación en la página maestra del tutorial Páginas maestras y navegación del sitio, se usó posicionamiento absoluto, es decir, se indicó el desplazamiento de píxeles exacto para la lista de navegación y el contenido principal. Como alternativa, CSS se puede usar para colocar un elemento a la derecha o a la izquierda de otro si se convierte en flotante. Para que la lista con viñetas de categorías aparezca a la izquierda de los productos de la categoría seleccionada, puede hacer que el control Repeater aparezca como elemento flotante a la izquierda de DataList.
Abra la página CategoriesAndProducts.aspx
desde la carpeta DataListRepeaterFiltering
y agregue a la página un control Repeater y otro DataList. Establezca el elemento ID
de Repeater en Categories
y el de DataList en CategoryProducts
. Vaya a la vista Origen y coloque los controles Repeater y DataList dentro de sus propios elementos <div>
. Es decir, incluya primero el control Repeater en un elemento <div>
y, después, DataList en su propio elemento <div>
, directamente después de Repeater. El marcado en este punto debe tener un aspecto similar al siguiente:
<div>
<asp:Repeater ID="Categories" runat="server">
</asp:Repeater>
</div>
<div>
<asp:DataList ID="CategoryProducts" runat="server">
</asp:DataList>
</div>
Para hacer flotante el control Repeater a la izquierda de DataList, es necesario usar el atributo de estilo CSS float
de esta forma:
<div>
Repeater
</div>
<div>
DataList
</div>
float: left;
hace flotar el primer elemento <div>
a la izquierda del segundo. Los valores width
y padding-right
indican el primer elemento width
de <div>
, y cuánto relleno va a haber entre el contenido del elemento <div>
y el margen derecho. Para más información sobre cómo hacer flotar elementos en CSS, vea Floatutorial.
En lugar de especificar el valor de estilo directamente desde el atributo style
del primer elemento <p>
, se creará una clase CSS denominada FloatLeft
en Styles.css
:
.FloatLeft
{
float: left;
width: 33%;
padding-right: 10px;
}
Después, puede reemplazar <div>
por <div class="FloatLeft">
.
Después de agregar la clase CSS y configurar el marcado en la página CategoriesAndProducts.aspx
, vaya al Diseñador. El control Repeater flotante debería aparecer a la izquierda de DataList (aunque ahora ambos solo aparecen como cuadros grises, ya que todavía hay que configurar sus orígenes de datos o plantillas).
Figura 2: El control Repeater flota a la izquierda de DataList (Haga clic para ver la imagen a tamaño completo)
Paso 2: Determinación del número de productos de cada categoría
Con el marcado circundante de Repeater y DataList completado, ya puede enlazar los datos de categoría al control Repeater. Pero como se muestra en la lista con viñetas de categorías de la figura 1, además de cada nombre de categoría, también es necesario mostrar el número de productos asociados a la categoría. Para acceder a esta información, puede hacer lo siguiente:
- Obtener esta información de la clase de código subyacente de la página ASP.NET. Dado un
categoryID
determinado, puede determinar el número de productos asociados si llama al métodoGetProductsByCategoryID(categoryID)
de la claseProductsBLL
. Este método devuelve un objetoProductsDataTable
cuya propiedadCount
indica el número deProductsRow
que hay, y que es el número de productos del elementocategoryID
especificado. Puede crear un controlador de eventosItemDataBound
para el control Repeater que, por cada categoría enlazada que tenga, llame al métodoGetProductsByCategoryID(categoryID)
de la claseProductsBLL
e incluya el recuento en la salida. - Actualice
CategoriesDataTable
en el objeto DataSet con tipo, de forma que incluya una columnaNumberOfProducts
. Después, puede actualizar el métodoGetCategories()
enCategoriesDataTable
para incluir esta información o, como alternativa, dejarGetCategories()
como está y crear un métodoCategoriesDataTable
denominadoGetCategoriesAndNumberOfProducts()
.
Ahora se explorarán las dos técnicas. El primer enfoque es más fácil de implementar, ya que no es necesario actualizar la capa de acceso a datos; pero necesita más comunicaciones con la base de datos. La llamada al método GetProductsByCategoryID(categoryID)
de la clase ProductsBLL
en el controlador de eventos ItemDataBound
agrega una llamada de base de datos adicional por cada categoría que se muestra en el control Repeater. Con esta técnica hay N+1 llamadas de base de datos, donde N es el número de categorías mostradas en el control Repeater. El segundo enfoque devuelve el recuento de productos con información sobre cada categoría del método GetCategories()
(o del método GetCategoriesAndNumberOfProducts()
) de la clase CategoriesBLL
, lo que da lugar a una sola comunicación con la base de datos.
Determinación del número de productos en el controlador de eventos ItemDataBound
Para determinar el número de productos de cada categoría en el controlador de eventos ItemDataBound
del control Repeater no se necesita ninguna modificación de la capa de acceso a datos existente. Todas las modificaciones se pueden realizar directamente en la página CategoriesAndProducts.aspx
. Para empezar, agregue un nuevo objeto ObjectDataSource denominado CategoriesDataSource
desde la etiqueta inteligente del control Repeater. Luego, configure ObjectDataSource CategoriesDataSource
para que recupere sus datos del método GetCategories()
de la clase CategoriesBLL
.
Figura 3: Configuración de ObjectDataSource para usar el método GetCategories()
de la clase CategoriesBLL
(Haga clic para ver la imagen a tamaño completo)
Se debe poder hacer clic en cada elemento del control Repeater Categories
y, cuando se haga clic en alguno, el control DataList CategoryProducts
debe mostrar esos productos de la categoría seleccionada. Esto se puede lograr si convierte cada categoría en un hipervínculo que lleve a esta misma página (CategoriesAndProducts.aspx
), pero que pase CategoryID
por la cadena de consulta, de forma muy similar a lo que ha visto en el tutorial anterior. La ventaja de este enfoque es que un motor de búsqueda puede marcar e indexar una página en la que se muestren productos de una categoría determinada.
Como alternativa, puede convertir cada categoría en un control LinkButton, que es el enfoque que se usará en este tutorial. El control LinkButton se representa en el explorador del usuario como un hipervínculo, pero, cuando se hace clic en él, induce un postback; en el postback, el objeto ObjectDataSource de DataList debe actualizarse para mostrar los productos que pertenecen a la categoría seleccionada. En este tutorial, el uso de un hipervínculo tiene más sentido que usar un control LinkButton; pero puede haber otros escenarios en los que el uso de controles LinkButton sea más ventajoso. Aunque el enfoque de hipervínculo sería ideal en este ejemplo, en su lugar se describirá la opción con LinkButton. Como verá, el uso de un control LinkButton presenta algunos retos que no surgirían con un hipervínculo. Por tanto, el uso de LinkButton en este tutorial planteará estos desafíos y le ayudará a proporcionar soluciones a esos escenarios en los que conviene usar un control LinkButton en lugar de un hipervínculo.
Nota:
Le animamos a repetir este tutorial, pero usar un control Hyperlink o un elemento <a>
en lugar de un control LinkButton.
En el siguiente marcado se muestra la sintaxis declarativa de los controles Repeater y ObjectDataSource. Fíjese en que las plantillas de Repeater muestran una lista con viñetas con cada elemento representado como un control LinkButton:
<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><asp:LinkButton runat="server" ID="ViewCategory"></asp:LinkButton></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
Nota:
En este tutorial, el control Repeater debe tener habilitado su estado de visualización (observe que EnableViewState="False"
se omite en la sintaxis declarativa de Repeater). En el paso 3 creará un controlador de eventos para el evento ItemCommand
de Repeater en el que se actualizará la colección SelectParameters
de la instancia ObjectDataSource de DataList. Pero el elemento ItemCommand
del Repeater no se activará si el estado de visualización está deshabilitado.
El control LinkButton con un valor de propiedad ID
de ViewCategory
no tiene su propiedad Text
establecida. Si solo hubiera querido mostrar el nombre de la categoría, habría establecido la propiedad Text mediante declaración, con la sintaxis de enlace de datos, de la siguiente manera:
<asp:LinkButton runat="server" ID="ViewCategory"
Text='<%# Eval("CategoryName") %>' />
Pero quiere mostrar el nombre de la categoría y el número de productos que pertenecen a esa categoría. Esta información se puede recuperar del controlador de eventos ItemDataBound
de Repeater con una llamada al método GetCategoriesByProductID(categoryID)
de la clase ProductBLL
, y determinar cuántos registros se devuelven en el elemento ProductsDataTable
resultante, como se muestra en el código siguiente:
Protected Sub Categories_ItemDataBound(sender As Object, e As RepeaterItemEventArgs)
' Make sure we're working with a data item...
If e.Item.ItemType = ListItemType.Item OrElse _
e.Item.ItemType = ListItemType.AlternatingItem Then
' Reference the CategoriesRow instance bound to this RepeaterItem
Dim category As Northwind.CategoriesRow = _
CType(CType(e.Item.DataItem, System.Data.DataRowView).Row, _
Northwind.CategoriesRow)
' Determine how many products are in this category
Dim productsAPI As New NorthwindTableAdapters.ProductsTableAdapter
Dim productCount As Integer = _
productsAPI.GetProductsByCategoryID(category.CategoryID).Count
' Reference the ViewCategory LinkButton and set its Text property
Dim ViewCategory As LinkButton = _
CType(e.Item.FindControl("ViewCategory"), LinkButton)
ViewCategory.Text = _
String.Format("{0} ({1:N0})", category.CategoryName, productCount)
End If
End Sub
Primero se asegurará de que trabaja con un elemento de datos (cuyo valor ItemType
es Item
o AlternatingItem
) y, después, hará referencia a la instancia de CategoriesRow
que acaba de enlazarse al objeto RepeaterItem
actual. A continuación, debe determinar el número de productos de esta categoría con la creación de una instancia de la clase ProductsBLL
, la llamada a su método GetCategoriesByProductID(categoryID)
y la determinación del número de registros devueltos mediante la propiedad Count
. Por último, se hace referencia al control LinkButton ViewCategory
de ItemTemplate, y su propiedad Text
se establece en CategoryName (NumberOfProductsInCategory), donde NumberOfProductsInCategory tiene un formato de número con cero posiciones decimales.
Nota:
Como alternativa, podría haber agregado una función de formato a la clase de código subyacente de la página ASP.NET que acepte los valores CategoryName
y CategoryID
de una categoría y devuelva el CategoryName
concatenado con el número de productos de la categoría (según lo determinado mediante una llamada al método GetCategoriesByProductID(categoryID)
). Los resultados de esta función de formato se podrían asignar mediante declaración a la propiedad Text de LinkButton, acabando así con la necesidad de usar el controlador de eventos ItemDataBound
. Consulte los tutoriales Uso de controles TemplateField en el control GridView o Aplicación de formato a DataList y Repeater en función de los datos para más información sobre cómo usar funciones de formato.
Después de agregar este controlador de eventos, dedique un momento a probar la página en un explorador. Observe cómo cada categoría aparece en una lista con viñetas que muestra el nombre de la categoría y el número de productos asociados a la categoría (vea la figura 4).
Figura 4: Se muestra cada nombre de categoría y el número de productos (Haga clic para ver la imagen a tamaño completo)
Actualización de CategoriesDataTable
y CategoriesTableAdapter
para incluir el número de productos de cada categoría
En lugar de determinar el número de productos de cada categoría a partir de su enlace con un control Repeater, se puede simplificar este proceso si se ajustan CategoriesDataTable
y CategoriesTableAdapter
en la capa de acceso a datos para incluir esta información de forma nativa. Para ello, debe agregar una nueva columna a CategoriesDataTable
para contener en ella el número de productos asociados. Para agregar una nueva columna a DataTable, abra el objeto DataSet con tipo (App_Code\DAL\Northwind.xsd
), haga clic con el botón derecho en el objeto DataTable que desea modificar y elija Agregar / Columna. Agregue una nueva columna a CategoriesDataTable
(vea la figura 5).
Figura 5: Adición de una nueva columna a CategoriesDataSource
(Haga clic para ver la imagen a tamaño completo)
Esto agregará una nueva columna denominada Column1
, nombre que puede cambiar simplemente escribiendo otro. Cambie el nombre de esta nueva columna a NumberOfProducts
. A continuación, es necesario configurar las propiedades de esta columna. Haga clic en la nueva columna y vaya a la ventana Propiedades. Cambie la propiedad DataType
de la columna de System.String
a System.Int32
y establezca la propiedad ReadOnly
en True
, como se muestra en la figura 6.
Figura 6: Establecimiento de las propiedades DataType
y ReadOnly
de la nueva columna
Aunque ahora CategoriesDataTable
tiene una columna NumberOfProducts
, el valor no se establece mediante ninguna de las consultas de TableAdapter correspondientes. Se puede actualizar el método GetCategories()
para devolver esta información para que se devuelva cada vez que se recupere información de la categoría. Pero si solo es necesario obtener el número de productos asociados de las categorías de forma muy ocasional (como simplemente para este tutorial, por ejemplo), se puede dejar GetCategories()
como está y crear un método que devuelva esta información. Se usará este último enfoque para crear un método denominado GetCategoriesAndNumberOfProducts()
.
Para agregar este nuevo método GetCategoriesAndNumberOfProducts()
, haga clic con el botón derecho en CategoriesTableAdapter
y seleccione Nueva consulta. Esto abre el asistente para la configuración de consultas de TableAdapter, que se ha usado numerosas veces en tutoriales anteriores. Para este método, inicie el asistente indicando que la consulta usa una instrucción SQL ad hoc que devuelve filas.
Figura 7: Creación del método mediante una instrucción SQL ad hoc (Haga clic para ver la imagen a tamaño completo)
Figura 8: La instrucción SQL devuelve filas (Haga clic para ver la imagen a tamaño completo)
En la siguiente pantalla del asistente se solicita la consulta que se va a usar. Para devolver los campos CategoryID
, CategoryName
y Description
de cada categoría, junto con el número de productos asociados a la categoría, use la siguiente instrucción SELECT
:
SELECT CategoryID, CategoryName, Description,
(SELECT COUNT(*) FROM Products p WHERE p.CategoryID = c.CategoryID)
as NumberOfProducts
FROM Categories c
Figura 9: Especificación de la consulta que se va a usar (Haga clic para ver la imagen a tamaño completo)
Fíjese en que la subconsulta que calcula el número de productos asociados a la categoría recibe el alias NumberOfProducts
. Esta coincidencia de nomenclatura hace que el valor devuelto por esta subconsulta se asocie a la columna NumberOfProducts
de CategoriesDataTable
.
Después de escribir la consulta, el último paso es elegir el nombre del nuevo método. Use FillWithNumberOfProducts
y GetCategoriesAndNumberOfProducts
en los campos Rellenar un DataTable y Devolver un DataTable, respectivamente.
Figura 10: Asignación de los nombres FillWithNumberOfProducts
y GetCategoriesAndNumberOfProducts
a los nuevos métodos de TableAdapter (Haga clic para ver la imagen a tamaño completo)
Llegado este punto, la capa de acceso a datos se ha ampliado para incluir el número de productos por categoría. Como la capa de presentación enruta todas las llamadas a la DAL mediante una capa de lógica de negocios independiente, es necesario agregar el correspondiente método GetCategoriesAndNumberOfProducts
a la clase CategoriesBLL
:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetCategoriesAndNumberOfProducts() As Northwind.CategoriesDataTable
Return Adapter.GetCategoriesAndNumberOfProducts()
End Function
Con la DAL y la BLL completadas, ya puede enlazar estos datos al control Repeater Categories
en CategoriesAndProducts.aspx
. Si ya ha creado una instancia de ObjectDataSource para el control Repeater en la sección Determinación del número de productos en el controlador de eventos ItemDataBound
, elimínelo y quite el valor de la propiedad DataSourceID
del control Repeater; asimismo, desasocie el evento ItemDataBound
del control Repeater del controlador de eventos quitando la sintaxis Handles Categories.OnItemDataBound
de la clase de código subyacente de ASP.NET.
Con el control Repeater de nuevo en su estado original, agregue un nuevo elemento ObjectDataSource denominado CategoriesDataSource
desde la etiqueta inteligente de Repeater. Configure ObjectDataSource para que use la clase CategoriesBLL
, pero en lugar de tener que usar el método GetCategories()
, haga que use GetCategoriesAndNumberOfProducts()
en su lugar (vea la figura 11).
Figura 11: Configuración de ObjectDataSource para usar el método GetCategoriesAndNumberOfProducts
(Haga clic para ver la imagen a tamaño completo)
A continuación, actualice ItemTemplate
para que la propiedad Text
de LinkButton se asigne mediante declaración usando la sintaxis de enlace de datos e incluya los campos de datos CategoryName
y NumberOfProducts
. Este es el marcado declarativo completo del control Repeater y e ObjectDataSource CategoriesDataSource
:
<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><asp:LinkButton runat="server" ID="ViewCategory"
Text='<%# String.Format("{0} ({1:N0})", _
Eval("CategoryName"), Eval("NumberOfProducts")) %>' />
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategoriesAndNumberOfProducts" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
La salida representada mediante la actualización de la DAL para incluir una columna NumberOfProducts
es la misma que si se usara el enfoque del controlador de eventos ItemDataBound
(vaya a la figura 4 para ver una captura de pantalla del control Repeater que muestra los nombres de categoría y el número de productos).
Paso 3: Representación de los productos de la categoría seleccionada
En este momento, en el control Repeater Categories
se muestra la lista de categorías junto con el número de productos de cada categoría. Repeater usa un control LinkButton en cada categoría que, cuando se presiona, provoca un postback, momento en el que es necesario mostrar esos productos de la categoría seleccionada en el control DataList CategoryProducts
.
Un reto que debe afrontar es cómo hacer que el control DataList muestre solo esos productos de la categoría seleccionada. En el tutorial Informe maestro/detalle mediante un control GridView maestro seleccionable con un control DetailView de detalles ha visto cómo crear un objeto GridView cuyas filas se pueden seleccionar y que los detalles de la fila seleccionada se mostraran en un control DetailsView en la misma página. El elemento ObjectDataSource de GridView devolvía información sobre todos los productos mediante el método GetProducts()
de ProductsBLL
, mientras que el elemento ObjectDataSource de DetailsView recuperaba información sobre el producto seleccionado mediante el método GetProductsByProductID(productID)
. El valor del parámetro productID
se obtenía mediante declaración al asociarlo con el valor de la propiedad SelectedValue
de GridView. Desafortunadamente, Repeater no tiene una propiedad SelectedValue
y no puede servir como origen de parámetros.
Nota:
Este es uno de los retos que surgen al usar LinkButton en un control Repeater. Si, en su lugar, se usa un hipervínculo que pase CategoryID
a través de la cadena de consulta, se podría usar ese campo QueryString como origen del valor del parámetro.
Antes de preocuparse por la falta de una propiedad SelectedValue
en Repeater, primero se enlazará el control DataList a un elemento ObjectDataSource y se especificará ItemTemplate
.
En la etiqueta inteligente de DataList, agregue un nuevo objeto ObjectDataSource llamado CategoryProductsDataSource
y configúrelo para usar el método GetProductsByCategoryID(categoryID)
de la clase ProductsBLL
. Como el control DataList de este tutorial ofrece una interfaz de solo lectura, no dude en establecer las listas desplegables en las pestañas INSERT, UPDATE y DELETE en (None).
Figura 12: Configuración de ObjectDataSource para usar el método GetProductsByCategoryID(categoryID)
de la clase ProductsBLL
(Haga clic para ver la imagen a tamaño completo)
Como el método GetProductsByCategoryID(categoryID)
espera un parámetro de entrada (categoryID
), el asistente para la configuración de orígenes de datos permite especificar el origen del parámetro. Si las categorías se hubieran enumerado en un control GridView o DataList, se habría establecido la lista desplegable Origen de parámetro en Control y ControlID, en el valor ID
del control web de datos. Pero como el control Repeater carece de una propiedad SelectedValue
, no se puede usar como origen de parámetros. Si se fija, verá que la lista desplegable ControlID solo contiene un control ID``CategoryProducts
, el valor ID
de DataList.
Por ahora, establezca la lista desplegable Origen de parámetro en Ninguno. Para finalizar, se asignará mediante programación este valor de parámetro cuando se haga clic en un control LinkButton de categoría de Repeater.
Figura 13: Origen de parámetro sin especificar para el parámetro categoryID
(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, Visual Studio genera automáticamente el objeto ItemTemplate
de DataList. Reemplace este valor ItemTemplate
predeterminado por la plantilla del tutorial anterior; establezca también la propiedad RepeatColumns
de DataList en 2. Después de realizar estos cambios, el marcado declarativo de DataList y su objeto ObjectDataSource asociado debe tener un aspecto similar al siguiente:
<asp:DataList ID="CategoryProducts" runat="server" DataKeyField="ProductID"
DataSourceID="CategoryProductsDataSource" RepeatColumns="2"
EnableViewState="False">
<ItemTemplate>
<h5><%# Eval("ProductName") %></h5>
<p>
Supplied by <%# Eval("SupplierName") %><br />
<%# Eval("UnitPrice", "{0:C}") %>
</p>
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="CategoryProductsDataSource"
OldValuesParameterFormatString="original_{0}" runat="server"
SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
<SelectParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
Actualmente, el parámetro categoryID
de ObjectDataSource CategoryProductsDataSource
no se ha configurado, por lo que no se muestra ningún producto al ver la página. Lo que debe hacer es configurar este valor de parámetro en función del valor CategoryID
de la categoría en la que se haga clic en el control Repeater. Esto plantea dos retos. Primero: ¿cómo se sabe cuándo se ha hecho clic en un control LinkButton en el objeto ItemTemplate
de Repeater? Y segundo: ¿cómo se puede determinar el valor CategoryID
de la categoría correspondiente en cuyo control LinkButton se ha hecho clic?
El control LinkButton, al igual que los controles Button e ImageButton, tiene un evento Click
y un evento Command
. El evento Click
está diseñado simplemente para señalar que se ha hecho clic en un control LinkButton. Pero en ocasiones, además de señalar que se ha hecho clic en un control LinkButton, también es necesario pasar información adicional al controlador de eventos. Si este es el caso, esta información adicional se puede asignar a las propiedades CommandName
y CommandArgument
de LinkButton. Así, cuando se haga clic en LinkButton, se activa su evento Command
(en lugar del evento Click
) y se pasan los valores de las propiedades CommandName
y CommandArgument
al controlador de eventos.
Cuando se genera un evento Command
desde una plantilla de Repeater, el evento ItemCommand
de Repeater se activa y se pasan los valores de CommandName
y CommandArgument
del control LinkButton (o Button, o ImageButton). Por tanto, para saber cuándo se ha hecho clic en el control LinkButton de una categoría en Repeater, es necesario hacer lo siguiente:
- Establezca la propiedad
CommandName
de LinkButton del objetoItemTemplate
de Repeater en un valor (aquí se ha usado ListProducts). Al establecer este valor deCommandName
, se activa el eventoCommand
de LinkButton cuando se haga clic en ese control. - Establezca la propiedad
CommandArgument
del control LinkButton en el valor deCategoryID
del elemento actual. - Cree un controlador de eventos para el evento
ItemCommand
del control Repeater. En el controlador de eventos, establezca el parámetroCategoryID
de ObjectDataSourceCategoryProductsDataSource
en el valor del objetoCommandArgument
pasado.
El siguiente marcado de ItemTemplate
del control Repeater de categorías implementa los pasos 1 y 2. Fíjese en cómo el valor de CommandArgument
se ha establecido en el valor CategoryID
del elemento de datos mediante la sintaxis de enlace de datos:
<ItemTemplate>
<li>
<asp:LinkButton CommandName="ListProducts" runat="server"
CommandArgument='<%# Eval("CategoryID") %>' ID="ViewCategory"
Text='<%# string.Format("{0} ({1:N0})", _
Eval("CategoryName"), Eval("NumberOfProducts")) %>'>
</asp:LinkButton>
</li>
</ItemTemplate>
Cada vez que cree un controlador de eventos ItemCommand
, es prudente comprobar el valor de CommandName
entrante, ya que cualquier evento Command
generado por cualquier control Button, LinkButton o ImageButton dentro de Repeater hará que se active el evento ItemCommand
. Aunque por ahora solo hay un control LinkButton de este tipo, en el futuro podría agregar más controles web Button a Repeater que, cuando se presionen, activen el mismo controlador de eventos ItemCommand
. Por tanto, es mejor asegurarse siempre de comprobar la propiedad CommandName
y continuar solo con la lógica de programación si coincide con el valor esperado.
Después de asegurarse de que el valor de CommandName
pasado es igual a ListProducts, el controlador de eventos asigna el parámetro CategoryID
de ObjectDataSource CategoryProductsDataSource
al valor del elemento CommandArgument
pasado. Esta modificación del elemento SelectParameters
de ObjectDataSource hace que el control DataList se vuelva a enlazar automáticamente al origen de datos y muestre los productos de la categoría recién seleccionada.
Protected Sub Categories_ItemCommand(source As Object, e As RepeaterCommandEventArgs) _
Handles Categories.ItemCommand
' If it's the "ListProducts" command that has been issued...
If String.Compare(e.CommandName, "ListProducts", True) = 0 Then
' Set the CategoryProductsDataSource ObjectDataSource's CategoryID parameter
' to the CategoryID of the category that was just clicked (e.CommandArgument)...
CategoryProductsDataSource.SelectParameters("CategoryID").DefaultValue = _
e.CommandArgument.ToString()
End If
End Sub
Con estas adiciones, el tutorial ha acabado. Dedique un momento a probarlo en un explorador. En la figura 14 se muestra la pantalla cuando se visita la página por primera vez. Como aún no se ha seleccionado una categoría, no se muestra ningún producto. Al hacer clic en una categoría, como Produce, se muestran los productos correspondientes en la categoría de productos, en una vista de dos columnas (vea la figura 15).
Figura 14: No se muestran productos al visitar la página por primera vez (Haga clic para ver la imagen a tamaño completo)
Figura 15: Al hacer clic en la categoría Produce, se muestran los productos coincidentes a la derecha (Haga clic para ver la imagen a tamaño completo)
Resumen
Como ha visto en este tutorial y en el anterior, los informes maestro/detalle se pueden distribuir entre dos páginas o condensarse en una sola. Pero mostrar un informe maestro/detalle en una sola página plantea algunos desafíos sobre cómo diseñar mejor los registros maestros y de detalles en la página. En el tutorial Informe maestro/detalle mediante un control GridView maestro seleccionable con un control DetailView de detalles hizo que los registros de detalles aparecieran encima de los registros maestros; en este tutorial, se usan técnicas de CSS para que los registros maestros floten a la izquierda de los detalles.
Junto con la representación de informes maestro/detalle, también ha tenido la oportunidad de explorar cómo recuperar el número de productos asociados a cada categoría, además de cómo ejecutar lógica del lado servidor cuando se hace clic en un control LinkButton (o Button, o ImageButton) desde un control Repeater.
Con este tutorial finaliza el análisis de informes maestro/detalle con DataList y Repeater. El siguiente lote de tutoriales ilustrará cómo agregar funcionalidades de edición y eliminación al control DataList.
¡Feliz programación!
Lecturas adicionales
Para más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:
- Floatutorial, un tutorial sobre elementos CSS flotantes con CSS
- Posicionamiento de CSS, donde encontrará más información sobre el posicionamiento de elementos con CSS
- Diseño de contenido con HTML mediante
<table>
y otros elementos HTML de posicionamiento
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. El revisor principal de este tutorial ha sido Zack Jones. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de