2010-07-08 16 views
13

Estoy tratando de usar un DataPager para hacer paginación del lado del servidor. Aquí está mi código¿Cómo uso un DataPager con paginación del lado del servidor?

<asp:DataPager ID="pgrFooBars" PagedControlID="lvFooBars" 
    QueryStringField="page" runat="server" > 
<Fields> 
    <asp:NumericPagerField /> 
</Fields> 
</asp:DataPager> 

Código Detrás

protected void Page_Load(object sender, EventArgs e) 
{ 
    ConfigureBlogPostListView(); 
} 

private void ConfigureBlogPostListView() 
{ 
    int pageNum; 
    int.TryParse(Request.Params["page"], out pageNum); 
    int pageSize = 20; 

    PagedList<IFooBar> FooBars = FooService.GetPagedFooBars(
     new PagingSettings(pageNum, pageSize)); 

    ListViewPagedDataSource ds = new ListViewPagedDataSource(); 
    ds.AllowServerPaging = true; 
    ds.DataSource = FooBars; 
    ds.MaximumRows = pageSize; 
    ds.StartRowIndex = pageNum; 
    //TotalCount is the total number of records in the entire set, not just those loaded. 
    ds.TotalRowCount = FooBars.TotalCount; 

    lvFooBars.DataSource = ds; 
    lvFooBars.DataBind(); 

    pgrFooBars.PageSize = pageSize; 
    pgrFooBars.SetPageProperties(pageNum, FooBars.TotalCount, true); 
} 

PagedList proviene de post útil de RobConery http://blog.wekeroad.com/2007/12/10/aspnet-mvc-pagedlistt/.

El problema es que el DataPager parece estar usando la propiedad Count del ListView para determinar el número total de registros, que en este caso es 20. De alguna manera, necesita saber que hay 1,500, no 20 registros totales . El DataPager tiene una propiedad TotalRowCount, pero esta es de solo lectura.

Nunca he visto un ejemplo de DataPager con paginación del lado del servidor, pero supuse que podría hacer paginación del lado del servidor; de lo contrario, ¿de qué sirve el atributo QueryStringField?

Soy consciente de que puede hacer una solución de paginación personalizada utilizando la metodología 4GuysFromRolla aquí http://www.4guysfromrolla.com/articles/031506-1.aspx, pero primero me gustaría saber si es posible una solución con el DataPager antes de crear una solución personalizada.

ACTUALIZACIÓN Cuanto más miro esto, más que estoy llegando a la conclusión de que esto no es posible y que, por desgracia, la DataPager es un control destinado únicamente para sitios web pequeños. Lo que quiero hacer realmente debería ser bastante simple si el control se construyó correctamente. Quiero ser capaz de decir

dpFooBars.TotalRowCountComputed = false; 
dpFooBars.TotalRowCount = AnyNumberThatISoChoose; 

que he estado buscando desde hace algún truco para obtener el mismo resultado, pero parece que TotalRowCount del DataPager se calcula a partir del número real de elementos de la fuente de datos que está destinado a . Me parece muy extraño que Microsoft creara una clase ListViewPagedDataSource() y un DataPager al mismo tiempo y no los hiciera funcionar correctamente juntos, pero esto parece haber sido lo que sucedió.

ACTUALIZACIÓN 2 (AHA momento?) Parece que ha sido posible hacer la paginación del lado del servidor desde .Net 2.0 mediante un ObjectDataSource y personalizar el SelectCountMethod(). Creo que debería ser posible personalizar ObjectDataSource para satisfacer mis necesidades. Hmmm. Me voy a ir por el fin de semana, así que me tomará un par de días ver si esto funciona. Estén atentos, verdaderos creyentes.

+0

¿Tu aplicación es la aplicación webform o Asp.Net MVC? – Sharique

Respuesta

3

Parece que la única manera de conseguir paginación del lado del servidor para trabajar con el DataPager es utilizar un LinqDataSource en su margen de beneficio (actualización: esto no es cierto, ver más abajo), y establecer el DataSourceID de su ListView (como en ScottGu's sample - step 6), no a través de la propiedad ListView DataSource. Sin embargo, descubrí que hay un trick para hacer que esto sea más viable para que pueda definir su consulta LinqDataSource a través de código subyacente en lugar de en el marcado (nota, el artículo dice que esto funcionará con cualquier control DataSource pero no lo creo es el caso).

Esto probablemente no será de ninguna ayuda para su situación ya que noté que llamaba a algún tipo de servicio que probablemente no devolvería (y no debería) un resultado IQueryable que es necesario para que esto funcione. Aquí es de todos modos en caso de que esté interesado ...

aspx marcado:

<asp:ListView ID="lvFooBars" DataSourceID="dsLinq" ....> 
    ...... 
</asp:ListView> 

<asp:DataPager ID="pgrFooBars" PagedControlID="lvFooBars" 
    QueryStringField="page" runat="server" > 
<Fields> 
    <asp:NumericPagerField /> 
</Fields> 
</asp:DataPager> 

<asp:LinqDataSource id="dsLinq" runat="server" OnSelecting="dsLinq_Selecting" /> 

de código subyacente:

protected void dsLinq_Selecting(object sender, LinqDataSourceSelectEventArgs e) 
{ 
    //notice this method on FooService requires no paging variables 
    e.Result = FooService.GetIQueryableFooBars(); 
} 

Nota, MSDN estados en lo que respecta a el atributo QueryStringField:

Establecer esta propiedad es útil si desea tener todas las páginas de datos indexadas por un motor de búsqueda. Este se produce porque el control produce una URL diferente para cada página de datos.

Actualización: de hecho se puede conseguir esto para trabajar usando un control ObjectDataSource en lugar del control LinqDataSource - ver this article and download the sample. Si bien utilizar un ObjectDataSource no es tan simple como usar un control LinqDataSource, usa menos cosas de linq mágico (en su lugar usa montones de cadenas mágicas que se asocian a métodos de negocios/capas de datos) y permite la exposición de IEnumerable para su método de acceso a datos en lugar de IQueryable.

Aún así, nunca he sido realmente partidario de incorporar ningún tipo de control DataSource en mi marcado UI (creo que esto es necesario), así que probablemente me mantendría alejado del control DataPager excepto para pequeñas aplicaciones como sugirió en tu primera actualización

John, otra cosa que noté fue que está intentando casarse con controles de servidor Asp.Net estándar (que dependen de ViewState) con la clase PagedList que se desarrolló como una clase auxiliar para aplicaciones Asp.Net Mvc.Si bien esto puede funcionar, puede haber rutas más simples para tomar.

+0

Gracias por consejo. La fascinación de Microsoft por acoplar estrechamente su capa de acceso a datos con la capa de aplicación continúa. – John

+1

Gracias, voy a echar un vistazo. Parece ser lo que estoy necesitando. Realmente no consideré que PagedList fuera una especie de MVC, simplemente algo más de Rob Conery. Antes de encontrar PagedList, las personas antes que yo habían estado usando métodos como List GetFoobars (string id, out TotalRowCount). Para mí, PagedList era solo una buena forma de O-O de hacer algo en lugar de usar una salida, con lo que nunca me siento cómodo. – John

+0

Creo que la ruta más simple sería usar MVC para empezar, pero el equipo aún no está listo para convertir este sitio (estamos empezando a utilizarlo en otro lugar). Mi sensación personal es que MVC es mucho más fácil que los formularios web en cualquier cosa que no sea el más simple de los sitios. – John

0

John, hice una pequeña prueba de concepto aquí para que pueda ver el código. Hice esto lo más básico posible para que no haya pelusa. Por favor, avíseme si hay algún atributo de esto que necesita que falta y lo revisaré.

cosas que se destacaron:

  1. los datos tienen que estar vinculado en el caso OnPreRender del buscapersonas cuando no se está utilizando un objeto DataSource (XML, SQL, acceso ...). Soy bonita seguro que este es su problema porque aquí es donde tuve más problemas.

  2. Viewstate debe estar habilitado en el ListView y el localizador (que es por defecto)

de código subyacente:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.UI; 
using System.Web.UI.WebControls; 

public partial class _Default : System.Web.UI.Page 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     if (!Page.IsPostBack) 
     { 
      this.BindListData(); 
     } 
    } 

    protected void dp_PreRender(object sender, EventArgs e) 
    { 
     this.BindListData(); 
    } 

    protected void BindListData() 
    { 
     List<Nums> n = new List<Nums>(); 
     for (int i = 0; i <= 100; i++) 
     { 
      n.Add(new Nums { Num1 = i, Num2 = i * 3 }); 
     } 

     this.lvMyGrid.DataSource = n; 
     this.lvMyGrid.DataBind(); 
    } 
} 

public class Nums 
{ 
    public int Num1 { get; set; } 
    public int Num2 { get; set; } 
} 

ASPX (a la izquierda de los demás pelusa):

<asp:DataPager OnPreRender="dp_PreRender" runat="server" ID="dpMyPager" QueryStringField="page" PagedControlID="lvMyGrid" PageSize="15"> 
    <Fields> 
     <asp:NumericPagerField /> 
    </Fields> 
</asp:DataPager> 

<asp:ListView runat="server" ID="lvMyGrid" DataKeyNames="Num1"> 
    <LayoutTemplate> 
     <asp:Literal runat="server" ID="itemPlaceholder" /> 
    </LayoutTemplate> 
    <ItemTemplate> 
     <div style="border: 1px solid red; padding: 3px; margin: 5px; float: left; clear: both;"> 
     <%# Eval("Num1") %> : <%# Eval("Num2") %> 
     </div> 
    </ItemTemplate> 
</asp:ListView> 

Disfrútalo, déjame saber si hay algo más en esto que necesites.

micrófono

+0

Desafortunadamente, esto no es lo que necesito. Necesito hacer paginación del lado del servidor, es decir, tengo 1000 registros totales en la base de datos, pero para ahorrar ancho de banda, solo voy a descargar 15 registros de la base de datos. Digamos que tengo 10.000 registros en la base de datos. No quiero tomar todos los 10,000 registros de la base de datos solo para que el datapager pueda obtener el recuento correcto. – John

+0

Ok, eso tiene sentido por qué esto es más complicado. Lo que probablemente tendrá que hacer es crear una colección personalizada como ICollection FooBarCollection y anular el método Count para que devuelva todo su conteo. No estoy seguro de si esto funcionará o no, pero es probable que tenga que hacer algo así para engañar al DataPager haciéndole creer que hay más datos allí que los que hay. – Jerzakie

10

Se necesita exactamente lo que usted hizo: paginación del servidor SQL que utiliza listview, datapager y fuente de datos linq. Como dices TotalRowCount es de solo lectura. También hay un método SetPageProperties que se puede usar para establecer el recuento total de filas, pero que tampoco me sirvió.

Sin embargo, logré engañarlo de esta manera. Tengo una vista de lista oculta simulada con una plantilla de elemento vacía. El localizador señalará esta vista de lista ficticia en lugar de la vista de lista original. Luego, debemos vincular la vista de lista ficticia a una colección ficticia con la misma cantidad de elementos que en la fuente de datos original. Esto asegurará la reproducción del buscapersonas correctamente. Aquí está el marcado.

<asp:DataPager ID="pdPagerBottom" runat="server" PagedControlID="lvDummy" PageSize="5" QueryStringField="page"> 
<Fields> 
    <asp:NumericPagerField ButtonType="Link" RenderNonBreakingSpacesBetweenControls="true" NextPageText="Next" PreviousPageText="Previous" ButtonCount="40" /> 
</Fields> 
</asp:DataPager> 

<asp:ListView ID="lvDummy" runat="server" Visible="false"> 
<ItemTemplate></ItemTemplate> 
</asp:ListView> 

<asp:ListView ID="lvPropertyList" runat="server">...</asp:ListView> 

y código detrás

 var datasourceQuery = context.Properties.OrderBy(x => x.PropertyID).Skip(pdPagerBottom.StartRowIndex).Take(pdPagerBottom.PageSize); 

     this.lvPropertyList.DataSource = datasourceQuery; 
     this.lvPropertyList.DataBind(); 

     this.lvDummy.DataSource = new List<int>(Enumerable.Range(0, context.Properties.Count())); 
     this.lvDummy.DataBind(); 

probado con 100 mil registros. Funciona un placer

+0

Solución interesante. Ahora estoy evitando el problema simplemente yendo por la ruta MVC :-) – John

+0

Esta respuesta es increíble ... rápida y fácil, y te ofrece todas las ventajas del buscapersonas de datos. Gracias – clamchoda

Cuestiones relacionadas