Ordenar datos en un control DataList o Repeater (VB)
Por Scott Mitchell
En este tutorial examinaremos cómo incluir compatibilidad con la ordenación en los controles DataList y Repeater y cómo crear un control DataList o Repeater cuyos datos se pueden paginar y ordenar.
Introducción
En el tutorial anterior vimos cómo agregar compatibilidad de paginación a un DataList. Creamos un nuevo método en la clase ProductsBLL
(GetProductsAsPagedDataSource
) que devolvía un objeto PagedDataSource
. Cuando se enlazaba a un DataList o Repeater, el DataList o Repeater mostraba solo la página de datos solicitada. Esta técnica es similar a la que usan internamente los controles GridView, DetailsView y FormView para proporcionar su funcionalidad de paginación predeterminada integrada.
Además de ofrecer compatibilidad con la paginación, el GridView también incluye compatibilidad con la ordenación lista para usar. Ni DataList ni Repeater proporcionan funcionalidades de ordenación integradas; sin embargo, con un poco de código se pueden agregar características de ordenación. En este tutorial examinaremos cómo incluir compatibilidad con la ordenación en los controles DataList y Repeater y cómo crear un control DataList o Repeater cuyos datos se pueden paginar y ordenar.
Un repaso de la ordenación
Como vimos en el tutorial Paginar y ordenar datos de informes, el control GridView proporciona compatibilidad de ordenación lista para usar. Cada campo del GridView puede tener un elemento SortExpression
asociado, que indica el campo de datos por el que se ordenan los datos. Cuando la propiedad AllowSorting
del GridView se establece en true
, cada campo del GridView que tiene un valor de propiedad SortExpression
tiene el encabezado representado como un control LinkButton. Cuando un usuario hace clic en un encabezado de campo del GridView determinado, se produce un postback y los datos se ordenan según el elemento SortExpression
del campo en el que se haya hecho clic en.
El control GridView también tiene una propiedad SortExpression
que almacena el elemento SortExpression
del campo del GridView por el que se ordenan los datos. Además, una propiedad SortDirection
indica si los datos se van a ordenar en orden ascendente o descendente (si un usuario hace clic dos veces seguidas en el vínculo de encabezado de un determinado de campo del GridView, se alterna el criterio de ordenación).
Cuando el GridView está enlazado a su control de origen de datos, entrega sus propiedades SortExpression
y SortDirection
al control de origen de datos. El control de origen de datos recupera los datos y, a continuación, los ordena según las propiedades SortExpression
y SortDirection
proporcionadas. Después de ordenar los datos, el control de origen de datos los devuelve al GridView.
Para replicar esta funcionalidad con los controles DataList o Repeater, debemos hacer lo siguiente:
- Crear una interfaz de ordenación
- Recordar el campo de datos por el que se va a ordenar y si se debe ordenar en orden ascendente o descendente
- Indicar al ObjectDataSource que ordene los datos por un campo de datos determinado
Abordaremos estas tres tareas en los pasos 3 y 4. Después, examinaremos cómo incluir compatibilidad de paginación y ordenación en un control DataList o Repeater.
Paso 2: Mostrar los productos en un control Repeater
Antes de preocuparnos por implementar cualquiera de las funciones relacionadas con la ordenación, comencemos enumerando los productos en un control Repeater. Para empezar, abra la página Sorting.aspx
en la carpeta PagingSortingDataListRepeater
. Agregue un control Repeater a la página web, estableciendo su propiedad ID
en SortableProducts
. En la etiqueta inteligente del Repeater, cree un nuevo objeto ObjectDataSource denominado ProductsDataSource
y configúrelo para recuperar datos del método GetProducts()
de la clase ProductsBLL
. Seleccione la opción (Ninguno) en las listas desplegables de las pestañas INSERTAR, ACTUALIZAR y ELIMINAR.
Figura 1: Crear un ObjectDataSource y configurarlo para que use el método GetProductsAsPagedDataSource()
(haga clic para ver la imagen a tamaño completo)
Figura 2: Establecimiento de las listas desplegables de las pestañas ACTUALIZAR, INSERTAR y ELIMINAR en (Ninguno) (haga clic para ver la imagen a tamaño completo)
A diferencia del DataList, Visual Studio no crea automáticamente un ItemTemplate
para el control Repeater después de enlazarlo a un origen de datos. Además, debemos agregar esto ItemTemplate
declarativamente, ya que la etiqueta inteligente del control Repeater carece de la opción Editar plantillas que sí tienen los controles DataList. Vamos a usar el mismo elemento ItemTemplate
del tutorial anterior, que muestra el nombre del producto, el proveedor y la categoría.
Después de agregar el elemento ItemTemplate
, el marcado declarativo del control Repeater y ObjectDataSource debe tener un aspecto similar al siguiente:
<asp:Repeater ID="SortableProducts" DataSourceID="ProductsDataSource"
EnableViewState="False" runat="server">
<ItemTemplate>
<h4><asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>'></asp:Label></h4>
Category:
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>'></asp:Label><br />
Supplier:
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>'></asp:Label><br />
<br />
<br />
</ItemTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProducts">
</asp:ObjectDataSource>
En la Figura 3, se muestra esta página vista desde un explorador.
Figura 3: Se muestra cada nombre de producto, proveedor y categoría (haga clic para ver la imagen a tamaño completo)
Paso 3: Indicar al ObjectDataSource que ordene los datos
Para ordenar los datos que se muestran en el control Repeater, es necesario informar al ObjectDataSource de la expresión de ordenación por la que se deben ordenar los datos. Antes de que ObjectDataSource recupere sus datos, primero activa su evento Selecting
, lo que proporciona una oportunidad para especificar una expresión de ordenación. Al controlador de eventos Selecting
se pasa un objeto de tipo ObjectDataSourceSelectingEventArgs
, que tiene una propiedad denominada Arguments
de tipo DataSourceSelectArguments
. La clase DataSourceSelectArguments
está diseñada para pasar solicitudes de datos desde un consumidor de datos al control de origen de datos, e incluye una propiedad SortExpression
.
Para pasar información de ordenación desde la página ASP.NET al ObjectDataSource, cree un controlador de eventos para el evento Selecting
y use el código siguiente:
Protected Sub ProductsDataSource_Selecting(ByVal sender As Object, _
ByVal e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSource.Selecting
e.Arguments.SortExpression = sortExpression
End Sub
El valor de sortExpression debe tener asignado el nombre del campo de datos por el que se van a ordenar los datos (por ejemplo, ProductName). No hay ninguna propiedad relacionada con el sentido de la ordenación, por lo que si desea ordenar los datos en orden descendente, anexe la cadena DESC al valor de sortExpression (por ejemplo, ProductName DESC).
Pruebe con algunos valores codificados de forma rígida para sortExpression y constate los resultados en un explorador. Como se muestra en la figura 4, cuando se usa ProductName DESC como valor de sortExpression, los productos se ordenan por nombre en orden alfabético inverso.
Figura 4: Los productos se ordenan por nombre en orden alfabético inverso (haga clic para ver la imagen a tamaño completo)
Paso 4: Crear la interfaz de ordenación y recordar la expresión y el sentido de la ordenación
Al activar la compatibilidad de ordenación en el GridView, cada texto de encabezado del campo susceptible de ordenación se convierte en un control LinkButton que, cuando se presiona, ordena los datos como corresponda. Esta interfaz de ordenación tiene sentido para el GridView, donde los datos están perfectamente dispuestos en columnas. Sin embargo, en los controles DataList y Repeater, se necesita una interfaz de ordenación diferente. Una interfaz de ordenación común para una lista de datos (en lugar de una tabla de datos) es una lista desplegable que proporciona los campos por los que se pueden ordenar los datos. Vamos a implementar esta interfaz en este tutorial.
Agregue un control web DropDownList encima del RepeaterSortableProducts
y establezca su propiedad ID
en SortBy
. En la ventana Propiedades, haga clic en los puntos suspensivos de la propiedad Items
, lo que abrirá el editor de la colección ListItem. Agregue varios ListItem
para ordenar los datos por los campos ProductName
, CategoryName
y SupplierName
. Agregue también un ListItem
para ordenar los productos por nombre en orden alfabético inverso.
Las propiedades Text
de ListItem
se pueden establecer en cualquier valor (como Name), pero las propiedades Value
deben establecerse en el nombre del campo de datos (como ProductName). Para ordenar los resultados en orden descendente, anexe la cadena DESC al nombre del campo de datos, como ProductName DESC.
Figura 5:: Adición de un ListItem
para cada uno de los campos de datos susceptibles de ordenación
Por último, agregue un control web Button a la derecha del DropDownList. Establezca su ID
en RefreshRepeater
y su propiedad Text
en Refresh.
Después de crear los ListItem
y agregar el botón de actualización, la sintaxis declarativa de DropDownList y Button debe ser similares a la siguiente:
<asp:DropDownList ID="SortBy" runat="server">
<asp:ListItem Value="ProductName">Name</asp:ListItem>
<asp:ListItem Value="ProductName DESC">Name (Reverse Order)
</asp:ListItem>
<asp:ListItem Value="CategoryName">Category</asp:ListItem>
<asp:ListItem Value="SupplierName">Supplier</asp:ListItem>
</asp:DropDownList>
<asp:Button runat="server" ID="RefreshRepeater" Text="Refresh" />
Con la ordenación de DropDownList completada, ahora necesitamos actualizar el controlador de eventos Selecting
del ObjectDataSource de forma que use la propiedad Value
del SortBy``ListItem
seleccionado, en lugar de una expresión de ordenación codificada de forma rígida.
Protected Sub ProductsDataSource_Selecting _
(ByVal sender As Object, ByVal e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSource.Selecting
' Have the ObjectDataSource sort the results by the
' selected sort expression
e.Arguments.SortExpression = SortBy.SelectedValue
End Sub
En este momento, cuando visite la página por primera vez, los productos se ordenarán inicialmente por el campo de datos ProductName
, ya que es el SortBy
ListItem
seleccionado de forma predeterminada (vea la figura 6). Si se selecciona otra opción de ordenación, como Category, y se hace clic en el botón de actualización, se producirá un postback y los datos se volverán a ordenar por nombre de la categoría, como se muestra en la figura 7.
Figura 6: Los productos están ordenados por nombre (haga clic aquí para ver la imagen a tamaño completo)
Figura 7: Los productos están ordenados por categoría (haga clic aquí para ver la imagen a tamaño completo)
Nota:
Al hacer clic en el botón de actualización, los datos se vuelven a ordenar automáticamente, porque el estado de visualización del control Repeater se ha deshabilitado, lo que hace que este vuelva a enlazarse a su origen de datos en cada postback. Si ha dejado habilitado el estado de visualización del control Repeater, cambiar la lista desplegable de ordenación no tendrá ningún efecto en el criterio de ordenación. Para solucionar este problema, cree un controlador de eventos para el evento Click
del botón de actualización y vuelva a enlazar el control Repeater a su origen de datos (llamando al método DataBind()
de dicho control).
Recordar la expresión y el sentido de la ordenación
Al crear un control DataList o Repeater susceptible de ordenación en una página en la que pueden producirse postbacks no relacionados con la ordenación, es imperativo recordar la expresión y el sentido de la ordenación de un postback a otro. Por ejemplo, imagine que hemos actualizado el control Repeater en este tutorial para incluir un botón Eliminar con cada producto. Cuando el usuario hace clic en el botón Eliminar, se ejecutaría código para eliminar el producto seleccionado y, a continuación, los datos se volverían a enlazar al control Repeater. Si los detalles de ordenación no se conservan en postback, los datos que se muestran en la pantalla volverán al criterio de ordenación original.
En este tutorial, el DropDownList guarda implícita y automáticamente la expresión y el sentido de la ordenación en su estado de visualización. Si estuviéramos usando una interfaz de ordenación diferente con, por ejemplo, controles LinkButton que proporcionaran distintas opciones de ordenación, tendríamos que tener cuidado de recordar el criterio de ordenación entre postbacks. Esto se puede lograr almacenando los parámetros de ordenación en el estado de visualización de la página, incluyendo el parámetro de ordenación en la cadena de consulta o a través de alguna otra técnica de persistencia de estado.
En ejemplos futuros de este tutorial se explora cómo conservar los detalles de ordenación en el estado de visualización de la página.
Paso 5: Agregar compatibilidad de ordenación a un control DataList que usa la paginación predeterminada
En el tutorial anterior vimos cómo implementar la paginación predeterminada en un DataList. Vamos a ampliar este ejemplo anterior para incluir funciones de ordenación de los datos paginados. Comience abriendo las páginas SortingWithDefaultPaging.aspx
y Paging.aspx
en la carpeta PagingSortingDataListRepeater
. En la página Paging.aspx
, haga clic en el botón Origen para ver el marcado declarativo de la página. Copie el texto seleccionado (vea la figura 8) y péguelo en el marcado declarativo de SortingWithDefaultPaging.aspx
entre etiquetas <asp:Content>
.
Figura 8: Replicación del marcado declarativo en las etiquetas <asp:Content>
de Paging.aspx
a SortingWithDefaultPaging.aspx
(haga clic para ver la imagen a tamaño completo)
Después de copiar el marcado declarativo, copie los métodos y las propiedades de la clase de código subyacente de la página Paging.aspx
en la clase de código subyacente de SortingWithDefaultPaging.aspx
. Ahora, dedique un momento a ver la página SortingWithDefaultPaging.aspx
en un explorador. Debe mostrar la misma funcionalidad y apariencia que Paging.aspx
.
Mejorar ProductsBLL para incluir un método de paginación y ordenación predeterminados
En el tutorial anterior creamos un método GetProductsAsPagedDataSource(pageIndex, pageSize)
en la clase ProductsBLL
que devolvió un objeto PagedDataSource
. Este objeto PagedDataSource
se rellenó con todos los productos (a través del método GetProducts()
de la BLL), pero al enlazarlo al DataList, solo se mostraban los registros correspondientes a los parámetros de entrada pageIndex y pageSize especificados.
Anteriormente en este tutorial agregamos compatibilidad de ordenación especificando la expresión de ordenación del controlador de eventos Selecting
del ObjectDataSource. Esto funciona bien cuando se devuelve un objeto a ObjectDataSource que se puede ordenar, como el objeto ProductsDataTable
devuelto por el método GetProducts()
. Sin embargo, el objeto PagedDataSource
devuelto por el método GetProductsAsPagedDataSource
no admite la ordenación de su origen de datos interno. En su lugar, es necesario ordenar los resultados devueltos por el método GetProducts()
antes de colocarlos en el PagedDataSource
.
Para ello, cree un nuevo método en la clase ProductsBLL
, GetProductsSortedAsPagedDataSource(sortExpression, pageIndex, pageSize)
. Para ordenar el objeto ProductsDataTable
devuelto por el método GetProducts()
, especifique la propiedad Sort
de su DataTableView
predeterminado:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsSortedAsPagedDataSource _
(sortExpression As String, pageIndex As Integer, pageSize As Integer) _
As PagedDataSource
' Get ALL of the products
Dim products As Northwind.ProductsDataTable = GetProducts()
'Sort the products
products.DefaultView.Sort = sortExpression
' Limit the results through a PagedDataSource
Dim pagedData As New PagedDataSource()
pagedData.DataSource = products.DefaultView
pagedData.AllowPaging = True
pagedData.CurrentPageIndex = pageIndex
pagedData.PageSize = pageSize
Return pagedData
End Function
El método GetProductsSortedAsPagedDataSource
difiere ligeramente del método GetProductsAsPagedDataSource
creado en el tutorial anterior. En concreto, GetProductsSortedAsPagedDataSource
acepta un parámetro de entrada adicional sortExpression
y asigna este valor a la propiedad Sort
del DefaultView
de ProductDataTable
. Unas cuantas líneas de código más adelante, al DataSource del objeto PagedDataSource
se le asigna el DefaultView
de ProductDataTable
.
Llamar al método GetProductsSortedAsPagedDataSource y especificar el valor del parámetro de entrada sortExpression
Con el método GetProductsSortedAsPagedDataSource
completado, el siguiente paso es proporcionar el valor de este parámetro. El ObjectDataSource en SortingWithDefaultPaging.aspx
está configurado actualmente para llamar al método GetProductsAsPagedDataSource
, y pasa los dos parámetros de entrada a través de sus QueryStringParameters
, que se especifican en la colección SelectParameters
. Estos dos elementos QueryStringParameters
indican que el origen de los parámetros pageIndex y pageSize del método GetProductsAsPagedDataSource
proceden de los elementos pageIndex
y pageSize
del campo de cadena de consulta.
Actualice la propiedad SelectMethod
del ObjectDataSource de forma que invoque el nuevo método GetProductsSortedAsPagedDataSource
. A continuación, agregue un QueryStringParameter
nuevo para acceder al parámetro de entrada sortExpression desde el elemento sortExpression
del campo de cadena de consulta. Establezca el elemento DefaultValue
de QueryStringParameter
en ProductName.
Después de estos cambios, el marcado declarativo de ObjectDataSource debe ser similar al siguiente:
<asp:ObjectDataSource ID="ProductsDefaultPagingDataSource"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProductsSortedAsPagedDataSource"
OnSelected="ProductsDefaultPagingDataSource_Selected" runat="server">
<SelectParameters>
<asp:QueryStringParameter DefaultValue="ProductName"
Name="sortExpression" QueryStringField="sortExpression"
Type="String" />
<asp:QueryStringParameter DefaultValue="0" Name="pageIndex"
QueryStringField="pageIndex" Type="Int32" />
<asp:QueryStringParameter DefaultValue="4" Name="pageSize"
QueryStringField="pageSize" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
En este momento, la página SortingWithDefaultPaging.aspx
ordenará los resultados alfabéticamente por nombre del producto (vea la figura 9). Esto se debe a que, de forma predeterminada, se pasa un valor de ProductName como parámetro sortExpression del método GetProductsSortedAsPagedDataSource
.
Figura 9: Los resultados se ordenan de forma predeterminada por ProductName
(haga clic para ver la imagen a tamaño completo)
Si agrega manualmente un campo de cadena de consulta de sortExpression
, como SortingWithDefaultPaging.aspx?sortExpression=CategoryName
, los resultados se ordenarán por el sortExpression
especificado. Sin embargo, este parámetro sortExpression
no se incluye en la cadena de consulta al pasar a una página de datos diferente. ¡De hecho, si hacemos clic en los botones Siguiente o Última página, volveremos a Paging.aspx
! Además, actualmente no hay ninguna interfaz de ordenación. La única manera de que un usuario pueda cambiar el criterio de ordenación de los datos paginados es manipulando la cadena de consulta directamente.
Crear la interfaz de ordenación
En primer lugar, es necesario actualizar el método RedirectUser
para dirigir al usuario a SortingWithDefaultPaging.aspx
(en lugar de a Paging.aspx
) e incluir el valor de sortExpression
en la cadena de consulta. También deberíamos agregar una propiedad SortExpression
con nombre, de solo lectura y de nivel de página. Esta propiedad, similar a las propiedades PageIndex
y PageSize
creadas en el tutorial anterior, devuelve el valor del campo de cadena de consulta sortExpression
, si existe, y el valor predeterminado (ProductName) si no.
Actualmente, el método RedirectUser
solo acepta un único parámetro de entrada: el índice de la página se va a mostrar. Sin embargo, puede haber ocasiones en las que queremos redirigir al usuario a una página determinada de datos mediante una expresión de ordenación distinta de la especificada en la cadena de consulta. En un momento, crearemos la interfaz de ordenación de esta página, que incluirá una serie de controles web Button para ordenar los datos por una columna especificada. Cuando se hace clic en uno de esos controles Button, queremos redirigir al usuario pasando el valor de expresión de ordenación adecuado. Para proporcionar esta funcionalidad, crearemos dos versiones del método RedirectUser
. La primera debe aceptar solo el índice de página que se va a mostrar, mientras que la segunda acepta el índice de página y la expresión de ordenación.
Private ReadOnly Property SortExpression() As String
Get
If Not String.IsNullOrEmpty(Request.QueryString("sortExpression")) Then
Return Request.QueryString("sortExpression")
Else
Return "ProductName"
End If
End Get
End Property
Private Sub RedirectUser(ByVal sendUserToPageIndex As Integer)
' Use the SortExpression property to get the sort expression
' from the querystring
RedirectUser(sendUserToPageIndex, SortExpression)
End Sub
Private Sub RedirectUser(ByVal sendUserToPageIndex As Integer,
ByVal sendUserSortingBy As String)
' Send the user to the requested page with the
' requested sort expression
Response.Redirect(String.Format("SortingWithDefaultPaging.aspx?" & _
"pageIndex={0}&pageSize={1}&sortExpression={2}", _
sendUserToPageIndex, PageSize, sendUserSortingBy))
End Sub
En el primer ejemplo de este tutorial, creamos una interfaz de ordenación con un DropDownList. En este ejemplo, vamos a usar tres controles web Button situados encima de DataList, uno para ordenar por ProductName
, uno para CategoryName
y otro para SupplierName
. Agregue los tres controles web Button, estableciendo sus propiedades ID
y Text
adecuadamente:
<p>
<asp:Button runat="server" id="SortByProductName"
Text="Sort by Product Name" />
<asp:Button runat="server" id="SortByCategoryName"
Text="Sort by Category" />
<asp:Button runat="server" id="SortBySupplierName"
Text="Sort by Supplier" />
</p>
A continuación, cree un controlador de eventos Click
para cada uno. Los controladores de eventos deben llamar al método RedirectUser
y devolver al usuario a la primera página mediante la expresión de ordenación adecuada.
Protected Sub SortByProductName_Click(sender As Object, e As EventArgs) _
Handles SortByProductName.Click
'Sort by ProductName
RedirectUser(0, "ProductName")
End Sub
Protected Sub SortByCategoryName_Click(sender As Object, e As EventArgs) _
Handles SortByCategoryName.Click
'Sort by CategoryName
RedirectUser(0, "CategoryName")
End Sub
Protected Sub SortBySupplierName_Click(sender As Object, e As EventArgs) _
Handles SortBySupplierName.Click
'Sort by SupplierName
RedirectUser(0, "SupplierName")
End Sub
Al visitar la página por primera vez, los datos se ordenan por el nombre del producto alfabéticamente (vea la figura 9). Haga clic en el botón Siguiente para avanzar a la segunda página de datos y, a continuación, haga clic en el botón Ordenar por categoría. Esto nos devuelve a la primera página de datos, ordenada por nombre de categoría (vea la figura 10). Del mismo modo, al hacer clic en el botón Ordenar por proveedor, se ordenan los datos por proveedor a partir de la primera página de datos. La opción de ordenación se recuerda a medida que los datos se paginan. En la figura 11 se muestra la página después de ordenar por categoría y avanzar a la decimotercera página de datos.
Figura 10: Los productos se ordenan por categoría (haga clic aquí para ver la imagen a tamaño completo)
Figura 11: La expresión de ordenación se recuerda al paginar por los datos (haga clic para ver la imagen a tamaño completo)
Paso 6: Paginación personalizada por los registros en un control Repeater
El ejemplo de DataList examinado en el paso 5 pagina por los datos mediante la técnica de paginación predeterminada, que no es muy eficaz. Al paginar por grandes cantidades de datos, es esencial usar la paginación personalizada. Si volvemos a los tutoriales Paginar de forma eficaz a través de grandes cantidades de datos y Ordenar datos paginados personalizados, en ellos vimos las diferencias entre paginación predeterminada y personalizada, y los métodos creados en la BLL para usar la paginación personalizada y ordenar los datos paginados personalizados. En concreto, en estos dos tutoriales anteriores agregamos los tres métodos siguientes a la clase ProductsBLL
:
GetProductsPaged(startRowIndex, maximumRows)
devuelve un determinado subconjunto de registros que empieza por startRowIndex y no supera maximumRows.GetProductsPagedAndSorted(sortExpression, startRowIndex, maximumRows)
devuelve un determinado subconjunto de registros ordenados por el parámetro de entrada sortExpression especificado.TotalNumberOfProducts()
proporciona el número total de registros en la tabla de base de datosProducts
.
Estos métodos se pueden usar para paginar y ordenar datos de forma eficaz mediante un control DataList o Repeater. Para ilustrar esto, comencemos creando un control Repeater con compatibilidad de paginación personalizada; después, agregaremos funcionalidades de ordenación.
Abra la página SortingWithCustomPaging.aspx
en la carpeta PagingSortingDataListRepeater
y agregue un control Repeater a la página, estableciendo su propiedad ID
en Products
. En la etiqueta inteligente del control Repeater, elija crear un objeto ObjectDataSource denominado ProductsDataSource
. Configúrelo para seleccionar los datos del método GetProductsPaged
de la clase ProductsBLL
.
Figura 12: Configuración de ObjectDataSource para usar el método GetProductsPaged
de la clase ProductsBLL
(haga clic para ver la imagen a tamaño completo)
Establezca las listas desplegables de las pestañas ACTUALIZAR, INSERTAR y ELIMINAR en (Ninguno) y, a continuación, haga clic en el botón Siguiente. Ahora, el asistente para la configuración de orígenes de datos solicita los orígenes de los parámetros de entrada startRowIndex y maximumRows del método GetProductsPaged
. En realidad, estos parámetros de entrada se omiten. En su lugar, los valores de startRowIndex y maximumRows se pasarán a través de la propiedad Arguments
en el controlador de eventos Selecting
del ObjectDataSource, sencillamente igual que como especificamos sortExpression en la primera demostración de este tutorial. Por lo tanto, deje las listas desplegables de orígenes de parámetros del asistente establecidas en Ninguno.
Figura 13: Los orígenes de parámetros se dejan establecidos en Ninguno (haga clic para ver la imagen a tamaño completo)
Nota:
No establezca la propiedad EnablePaging
del ObjectDataSource en true
. Esto hará que el ObjectDataSource incluya automáticamente sus propios parámetros startRowIndex y maximumRows en la lista de parámetros existente de SelectMethod
. La propiedad EnablePaging
es útil al enlazar datos paginados personalizados a un control GridView, DetailsView o FormView, ya que estos controles esperan un determinado comportamiento del ObjectDataSource que solo está disponible cuando la propiedad EnablePaging
es true
. Como tenemos que agregar manualmente la compatibilidad de paginación para DataList y Repeater, deje esta propiedad establecida en false
(el valor predeterminado), ya que crearemos la funcionalidad necesaria directamente dentro de la página ASP.NET.
Por último, defina el elemento ItemTemplate
del control Repeater para mostrar el nombre, la categoría y el proveedor del producto. Después de realizar estos dos cambios, la sintaxis declarativa del Repeater y el ObjectDataSource debe ser similar a la siguiente:
<asp:Repeater ID="Products" runat="server" DataSourceID="ProductsDataSource"
EnableViewState="False">
<ItemTemplate>
<h4><asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>'></asp:Label></h4>
Category:
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>'></asp:Label><br />
Supplier:
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>'></asp:Label><br />
<br />
<br />
</ItemTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProductsPaged" TypeName="ProductsBLL">
<SelectParameters>
<asp:Parameter Name="startRowIndex" Type="Int32" />
<asp:Parameter Name="maximumRows" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
Dedique un momento a visitar la página a través de un explorador y fíjese en que no se devuelve ningún registro. Esto se debe a que todavía tenemos que especificar los valores de parámetro de startRowIndex y maximumRows, de ahí que se pase un valor de 0 en ambos. Para especificar estos valores, cree un controlador de eventos para el evento Selecting
del ObjectDataSource y establezca estos valores de parámetro mediante programación en unos valores codificados de forma rígida de 0 y 5 respectivamente:
Protected Sub ProductsDataSource_Selecting(sender As Object, _
e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSource.Selecting
e.InputParameters("startRowIndex") = 0
e.InputParameters("maximumRows") = 5
End Sub
Con este cambio, cuando la página se visualice a través de un explorador, muestra los cinco primeros productos.
Figura 14: Se muestran los cinco primeros registros (haga clic para ver la imagen a tamaño completo)
Nota:
Los productos que se muestran en la figura 14 se ordenan por nombre de producto porque el procedimiento almacenado GetProductsPaged
que realiza la consulta de paginación personalizada eficaz ordena los resultados por ProductName
.
Para permitir que el usuario recorra las páginas, es necesario realizar un seguimiento del índice de fila de inicio y las filas máximas, y recordar estos valores entre postbacks. En el ejemplo de paginación predeterminado, usamos campos de cadena de consulta para conservar estos valores; en esta demostración, vamos a conservar esta información en el estado de visualización de la página. Cree las dos propiedades siguientes:
Private Property StartRowIndex() As Integer
Get
Dim o As Object = ViewState("StartRowIndex")
If o Is Nothing Then
Return 0
Else
Return CType(o, Integer)
End If
End Get
Set(ByVal value As Integer)
ViewState("StartRowIndex") = value
End Set
End Property
Private Property MaximumRows() As Integer
Get
Dim o As Object = ViewState("MaximumRows")
If o Is Nothing Then
Return 5
Else
Return CType(o, Integer)
End If
End Get
Set(ByVal value As Integer)
ViewState("MaximumRows") = value
End Set
End Property
A continuación, actualice el código en el controlador de eventos Selecting para que use las propiedades StartRowIndex
y MaximumRows
en lugar de los valores codificados de forma rígida de 0 y 5:
e.InputParameters("startRowIndex") = 0
e.InputParameters("maximumRows") = 5
En este momento, nuestra página todavía sigue mostrando solo los cinco primeros registros. Sin embargo, con estas propiedades presentes, estaremos listos para crear la interfaz de paginación.
Agregar la interfaz de paginación
Vamos a usar la misma interfaz de paginación (Primera, Anterior, Siguiente, Última) usada en el ejemplo de paginación predeterminado, incluido el control web Label que muestra qué página de datos se está viendo y cuántas páginas totales existen. Agregue los cuatro controles web Button y el control Label debajo del Repeater.
<p>
<asp:Button runat="server" ID="FirstPage" Text="<< First" />
<asp:Button runat="server" ID="PrevPage" Text="< Prev" />
<asp:Button runat="server" ID="NextPage" Text="Next >" />
<asp:Button runat="server" ID="LastPage" Text="Last >>" />
</p>
<p>
<asp:Label runat="server" ID="CurrentPageNumber"></asp:Label>
</p>
A continuación, cree controladores de eventos Click
para los cuatro botones. Cuando se haga clic en uno de estos botones, es necesario actualizar StartRowIndex
y volver a enlazar los datos al control Repeater. El código de los botones Primera, Anterior y Siguiente es sencillo, pero en cuanto al botón Última, ¿cómo se determina el índice de fila inicial de la última página de datos? Para calcular este índice, así como para determinar si se deben habilitar los botones Siguiente y Última, es necesario saber cuántos registros en total se paginan. Podemos determinarlo llamando al método TotalNumberOfProducts()
de la clase ProductsBLL
. Vamos a crear una propiedad de solo lectura y de nivel de página denominada TotalRowCount
que devuelve los resultados del método TotalNumberOfProducts()
:
Private ReadOnly Property TotalRowCount() As Integer
Get
'Return the value from the TotalNumberOfProducts() method
Dim productsAPI As New ProductsBLL()
Return productsAPI.TotalNumberOfProducts()
End Get
End Property
Con esta propiedad, ahora podemos determinar el índice de fila de inicio de la última página. En concreto, es un resultado de número entero de TotalRowCount
menos 1, dividido entre MaximumRows
y multiplicado por MaximumRows
. Ahora podemos escribir los controladores de eventos Click
de los cuatro botones de la interfaz de paginación:
Protected Sub FirstPage_Click(sender As Object, e As EventArgs) _
Handles FirstPage.Click
'Return to StartRowIndex of 0 and rebind data
StartRowIndex = 0
Products.DataBind()
End Sub
Protected Sub PrevPage_Click(sender As Object, e As EventArgs) _
Handles PrevPage.Click
'Subtract MaximumRows from StartRowIndex and rebind data
StartRowIndex -= MaximumRows
Products.DataBind()
End Sub
Protected Sub NextPage_Click(sender As Object, e As EventArgs) _
Handles NextPage.Click
'Add MaximumRows to StartRowIndex and rebind data
StartRowIndex += MaximumRows
Products.DataBind()
End Sub
Protected Sub LastPage_Click(sender As Object, e As EventArgs) _
Handles LastPage.Click
'Set StartRowIndex = to last page's starting row index and rebind data
StartRowIndex = ((TotalRowCount - 1) \ MaximumRows) * MaximumRows
Products.DataBind()
End Sub
Por último, debemos deshabilitar los botones Primera y Anterior en la interfaz de paginación al ver la primera página de datos y los botones Siguiente y Última al ver la última. Para ello, agregue el código siguiente al controlador de eventos Selecting
del ObjectDataSource:
' Disable the paging interface buttons, if needed
FirstPage.Enabled = StartRowIndex <> 0
PrevPage.Enabled = StartRowIndex <> 0
Dim LastPageStartRowIndex As Integer = _
((TotalRowCount - 1) \ MaximumRows) * MaximumRows
NextPage.Enabled = (StartRowIndex < LastPageStartRowIndex)
LastPage.Enabled = (StartRowIndex < LastPageStartRowIndex)
Después de agregar estos controladores de eventos Click
y el código para habilitar o deshabilitar los elementos de la interfaz de paginación según el índice de fila de inicio actual, pruebe la página en un explorador. Como se muestra en la figura 15, al visitar por primera vez la página, se deshabilitarán los botones Primera y Anterior. Al hacer clic en Siguiente, se muestra la segunda página de datos, mientras que al hacer clic en Última, se muestra la última página (vea las figuras 16 y 17). Al ver la última página de datos, los botones Siguiente y Última están deshabilitados.
Figura 15: Los botones Anterior y Última están deshabilitados al ver la primera página de productos (haga clic para ver la imagen a tamaño completo)
Figura 16: Se muestra la segunda página de productos (haga clic para ver la imagen a tamaño completo)
Figura 17:: Al hacer clic en Última, se muestra la última página de datos (haga clic para ver la imagen a tamaño completo)
Paso 7: Incluir compatibilidad de ordenación con el control Repeater paginado personalizado
Ahora que hemos implementado la paginación personalizada, estamos listos para incluir compatibilidad con la ordenación. El método GetProductsPagedAndSorted
de la clase ProductsBLL
tiene los mismos parámetros de entrada startRowIndex y maximumRows que GetProductsPaged
, pero permite un parámetro de entrada sortExpression más. Para usar el método GetProductsPagedAndSorted
de SortingWithCustomPaging.aspx
, es necesario realizar los pasos siguientes:
- Cambiar la propiedad
SelectMethod
del ObjectDataSource deGetProductsPaged
aGetProductsPagedAndSorted
- Agregar un objeto sortExpression
Parameter
a la colecciónSelectParameters
del ObjectDataSource - Crear una propiedad
SortExpression
privada y de nivel de página que conserve su valor entre postbacks a través del estado de visualización de la página - Actualizar el controlador de eventos
Selecting
del ObjectDataSource para asignar al parámetro sortExpression del ObjectDataSource el valor de la propiedadSortExpression
de nivel de página - Crear la interfaz de ordenación
Comience actualizando la propiedad SelectMethod
del ObjectDataSource; para ello, agregue un objeto sortExpressionParameter
. Asegúrese de que la propiedad Type
de Parameter
del objeto sortExpression está establecida en String
. Después de completar estas dos primeras tareas, el marcado declarativo del control ObjectDataSource debería tener un aspecto similar al siguiente:
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProductsPagedAndSorted"
OnSelecting="ProductsDataSource_Selecting">
<SelectParameters>
<asp:Parameter Name="sortExpression" Type="String" />
<asp:Parameter Name="startRowIndex" Type="Int32" />
<asp:Parameter Name="maximumRows" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
A continuación, necesitamos una propiedad SortExpression
de nivel de página cuyo valor se serialice en el estado de visualización. Si no se ha establecido ningún valor de expresión de ordenación, use ProductName como valor predeterminado:
Private Property SortExpression() As String
Get
Dim o As Object = ViewState("SortExpression")
If o Is Nothing Then
Return "ProductName"
Else
Return o.ToString()
End If
End Get
Set(ByVal value As String)
ViewState("SortExpression") = value
End Set
End Property
Antes de que ObjectDataSource invoque el método GetProductsPagedAndSorted
, necesitamos establecer sortExpressionParameter
en el valor de la propiedad SortExpression
. En el controlador de eventos Selecting
, agregue la siguiente línea de código:
e.InputParameters("sortExpression") = SortExpression
Lo único que queda es implementar la interfaz de ordenación. Como hicimos en el último ejemplo, vamos a implementarla mediante tres controles web Button que permiten al usuario ordenar los resultados por nombre de producto, categoría o proveedor.
<asp:Button runat="server" id="SortByProductName"
Text="Sort by Product Name" />
<asp:Button runat="server" id="SortByCategoryName"
Text="Sort by Category" />
<asp:Button runat="server" id="SortBySupplierName"
Text="Sort by Supplier" />
Cree controladores de eventos Click
para estos tres controles Button. En el controlador de eventos, restablezca StartRowIndex
a 0, establezca SortExpression
en el valor adecuado y vuelva a enlazar los datos al control Repeater:
Protected Sub SortByProductName_Click(sender As Object, e As EventArgs) _
Handles SortByProductName.Click
StartRowIndex = 0
SortExpression = "ProductName"
Products.DataBind()
End Sub
Protected Sub SortByCategoryName_Click(sender As Object, e As EventArgs) _
Handles SortByCategoryName.Click
StartRowIndex = 0
SortExpression = "CategoryName"
Products.DataBind()
End Sub
Protected Sub SortBySupplierName_Click(sender As Object, e As EventArgs) _
Handles SortBySupplierName.Click
StartRowIndex = 0
SortExpression = "CompanyName"
Products.DataBind()
End Sub
Así de simple. Aunque han sido necesarios una serie de pasos para implementar la paginación y ordenación personalizadas, fueron muy similares a los de la paginación predeterminada. En la figura 18 se muestran los productos al ver la última página de datos cuando se ordenan por categoría.
Figura 18: Se muestra la última página de datos ordenada por categoría (haga clic para ver la imagen a tamaño completo)
Nota:
En ejemplos anteriores, al ordenar por proveedor, se usaba el valor SupplierName como expresión de ordenación. Sin embargo, en la implementación de paginación personalizada, debemos usar CompanyName. Esto se debe a que el procedimiento almacenado encargado de implementar la paginación personalizada, GetProductsPagedAndSorted
, pasa la expresión de ordenación en la palabra clave ROW_NUMBER()
, y la palabra clave ROW_NUMBER()
requiere el nombre de columna real en lugar de un alias. Por lo tanto, debemos usar CompanyName
(el nombre de la columna de la tabla Suppliers
) en lugar del alias usado en la consulta SELECT
(SupplierName
) en la expresión de ordenación.
Resumen
Ni DataList ni Repeater ofrecen compatibilidad de ordenación integrada, pero con un poco de código y una interfaz de ordenación personalizada, se puede agregar dicha funcionalidad. Al implementar ordenación, pero no paginación, la expresión de ordenación se puede especificar a través del objeto DataSourceSelectArguments
pasado al método Select
del ObjectDataSource. Esta propiedad DataSourceSelectArguments
del objeto SortExpression
se puede asignar en el controlador de eventos Selecting
del ObjectDataSource.
Para agregar capacidades de ordenación a un control DataList o Repeater que ya proporciona compatibilidad con la paginación, el enfoque más sencillo es personalizar la capa de lógica empresarial de manera que incluya un método que acepte una expresión de ordenación. A continuación, esta información se puede pasar a través de un parámetro en el elemento SelectParameters
del ObjectDataSource.
Este tutorial completa nuestro análisis sobre la paginación y ordenación con los controles DataList y Repeater. En el siguiente —y último— tutorial veremos cómo agregar controles web Button a las plantillas de DataList y Repeater para proporcionar algo de funcionalidad personalizada iniciada por el usuario según cada elemento.
¡Feliz programación!
Acerca del autor
Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, lleva 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 a través de mitchell@4GuysFromRolla.com. o a través de su blog, que se puede encontrar en http://ScottOnWriting.NET.
Agradecimientos especiales a
Muchos revisores han evaluado esta serie de tutoriales. El revisor principal de este tutorial ha sido David Suru. ¿Le interesa 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