我想我有一些东西至少可以作为进一步探索的一个很好的起点。我制作了一个示例(进一步向下)来说明我的方法,该方法基于几个想法:
- 为了使分页工作,
ObjectDatasource
应该使用。这样就可以知道GridView
总共有多少行。
- 我们需要一种方法来让我们
ObjectDataSource
访问我们获取的数据一旦可用。
为了解决2.我想出的想法是定义一个界面,该界面所在的页面GridView
可以实现。然后ObjectDataSource
可以使用一个类将获取数据的调用中继到页面本身。过早调用时,会返回空数据,但稍后会被真实数据替换。
让我们看一些代码。
这是我的 aspx 文件:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeFile="GridViewTest.aspx.cs" Inherits="GridViewTest" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="Server">
<asp:GridView ID="jobsGv" runat="server" AutoGenerateColumns="false" AllowPaging="true"
PageSize="13" OnPageIndexChanging="jobsGv_PageIndexChanging" DataSourceID="jobsDataSource">
<Columns>
<asp:TemplateField HeaderText="Job Id">
<ItemTemplate>
<asp:Literal ID="JobId" runat="server" Text='<%# Eval("JobId") %>'></asp:Literal>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Job description">
<ItemTemplate>
<asp:Literal ID="Description" runat="server" Text='<%# Eval("Description") %>'></asp:Literal>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Min level">
<ItemTemplate>
<asp:Literal ID="MinLvl" runat="server" Text='<%# Eval("MinLvl") %>'></asp:Literal>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="jobsDataSource" runat="server" TypeName="JobObjectDs" CacheDuration="0"
SelectMethod="GetJobs" EnablePaging="True" SelectCountMethod="GetTotalJobsCount">
</asp:ObjectDataSource>
<asp:Button ID="button" runat="server" OnClick="button_Click" Text="Test postback" />
</asp:Content>
以及背后的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI.WebControls;
public partial class GridViewTest : System.Web.UI.Page, IJobDsPage
{
bool gridNeedsBinding = false;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
gridNeedsBinding = true;
}
}
protected void jobsGv_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
var gv = (GridView)sender;
newPageIndexForGv = e.NewPageIndex;
gridNeedsBinding = true;
}
private int newPageIndexForGv = 0;
protected void Page_PreRendercomplete(object sender, EventArgs e)
{
if (gridNeedsBinding)
{
// fetch data into this.jobs and this.totalJobsCount to simulate
// that data has just become available asynchronously
JobDal dal = new JobDal();
jobs = dal.GetJobs(jobsGv.PageSize, jobsGv.PageSize * newPageIndexForGv).ToList();
totalJobsCount = dal.GetTotalJobsCount();
//now that data is available, bind gridview
jobsGv.DataBind();
jobsGv.SetPageIndex(newPageIndexForGv);
}
}
#region JobDsPage Members
List<Job> jobs = new List<Job>();
public IEnumerable<Job> GetJobs()
{
return jobs;
}
public IEnumerable<Job> GetJobs(int maximumRows, int startRowIndex)
{
return jobs;
}
int totalJobsCount;
public int GetTotalJobsCount()
{
return totalJobsCount;
}
#endregion
protected void button_Click(object sender, EventArgs e)
{
}
}
最后是一些将它们联系在一起的类。我在 App_Code 的一个代码文件中将这些组合在一起:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Simple POCO to use as row data in GridView
/// </summary>
public class Job
{
public int JobId { get; set; }
public string Description { get; set; }
public int MinLvl { get; set; }
//etc
}
/// <summary>
/// This will simulate a DAL that fetches data
/// </summary>
public class JobDal
{
private static int totalCount = 50; // let's pretend that db has total of 50 job records
public IEnumerable<Job> GetJobs()
{
return Enumerable.Range(0, totalCount).Select(i =>
new Job() { JobId = i, Description = "Descr " + i, MinLvl = i % 10 }); //simulate getting all records
}
public IEnumerable<Job> GetJobs(int maximumRows, int startRowIndex)
{
int count = (startRowIndex + maximumRows) > totalCount ? totalCount - startRowIndex : maximumRows;
return Enumerable.Range(startRowIndex, count).Select(i =>
new Job() { JobId = i, Description = "Descr " + i, MinLvl = i % 10 }); //simulate getting one page of records
}
public int GetTotalJobsCount()
{
return totalCount; // simulate counting total amount of rows
}
}
/// <summary>
/// Interface for our page, so we can call methods in the page itself
/// </summary>
public interface IJobDsPage
{
IEnumerable<Job> GetJobs();
IEnumerable<Job> GetJobs(int maximumRows, int startRowIndex);
int GetTotalJobsCount();
}
/// <summary>
/// This will be used by our ObjectDataSource
/// </summary>
public class JobObjectDs
{
public IEnumerable<Job> GetJobs()
{
var currentPageAsIJobDsPage = (IJobDsPage)HttpContext.Current.CurrentHandler;
return currentPageAsIJobDsPage.GetJobs();
}
public IEnumerable<Job> GetJobs(int maximumRows, int startRowIndex)
{
var currentPageAsIJobDsPage = (IJobDsPage)HttpContext.Current.CurrentHandler;
return currentPageAsIJobDsPage.GetJobs(maximumRows, startRowIndex);
}
public int GetTotalJobsCount()
{
var currentPageAsIJobDsPage = (IJobDsPage)HttpContext.Current.CurrentHandler;
return currentPageAsIJobDsPage.GetTotalJobsCount();
}
}
那么这一切有什么作用呢?
好吧,我们有实现IJobDsPage
接口的 Page。在页面上,我们有GridView
使用ObjectDataSource
with id的页面jobsDataSource
。这反过来又使用类JobObjectDs
来获取数据。并且该类依次从 中获取当前正在执行的 PageHttpContext
并将其转换为IJobDsPage
接口并调用 Page 上的接口方法。
所以结果是我们有一个GridView
which 使用一个ObjectDataSource
which 调用 Page 中的方法来获取数据。如果过早调用这些方法,则会返回空数据(new List<Job>()
相应的总行数为零)。但这没有问题,因为当数据可用时,当我们到达页面处理的一个阶段时,我们无论如何都要手动绑定 GridView。
总而言之,我的样本作品虽然远非一流。就目前而言,将在请求期间多次ObjectDataSource
调用其关联的方法。Select
不过,这并不像最初听起来那么糟糕,因为数据的实际提取仍然只会发生一次。此外,GridView
当切换到下一页时,将两次绑定到相同的数据。
所以还有改进的余地。但这至少是一个起点。