快速 GridView
一个快速且优化的 GridView。
引言
GridView
控制是一个非常强大且可扩展的 .NET 控制。您可以在应用程序中使用它来列出实体。但是,当您想要使用分页功能时,存在一个缺点:您必须系统地将 GridView
与所有数据的选择绑定,以便在更改索引时查看其他页面。想象一下,您必须使用一个包含 400,000 条记录的数据源:由于处理未优化,回发会非常耗费资源。
背景
我尝试提出一个 GridView
实现,该实现具有独立的分页功能。目的是将 GridView
与您看到的当前 DataRow
绑定。SQL Server 允许您使用以下语法获取查询的部分行:
With Prod AS
( SELECT [ProductID],
[ProductName],
[SupplierID],
[CategoryID],
[QuantityPerUnit],
[UnitPrice],
[UnitsInStock],
[UnitsOnOrder],
[ReorderLevel],
[Discontinued] ,
ROW_NUMBER() OVER (order by ProductName) as RowNumber from Products )
SELECT [ProductID],
[ProductName],
[SupplierID],
[CategoryID],
[QuantityPerUnit],
[UnitPrice],
[UnitsInStock],
[UnitsOnOrder],
[ReorderLevel],
[Discontinued] from Prod Where RowNumber
Between @pBegin and @pEnd
我在 CodeProject 上搜索,看看是否存在类似的解决方案,我找到一个源代码,它实现了一个非常智能的分页器。您可以在这里找到它:https://codeproject.org.cn/KB/aspnet/SmartPager.aspx。我扩展了 GridView
类以创建 GridviewPager
类,该类拥有此 SmartPager
控制的实例。SmartPager
控制是一个*复杂类型属性(它具有多个属性)*,因此分页器类必须实现 IStateManager
接口和一个事件,以了解索引何时更改。
SmartPagerSelectPage
方法),当索引更改时会执行两个回发。为了解决这个问题,我解析了所有请求表单键,以找到 CALLBACKPARAM
值,以便过滤掉错误的回发,并在 GridviewPager
Page_Load
事件中仅执行正确的回发。
Using the Code
using System;
using System.Net;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Avg.Controls;
/// <summary>
/// Summary description for GridViewExtender
/// </summary>
///
namespace Avg.Controls
{
public delegate void DataSourceChangedEventHandler(object sender, bool isEmpty);
[ToolboxData("<{0}:GridViewPager runat="server">")]
public class GridViewPager : GridView
{
/// <summary>
/// Handling when the DataSourceChange
/// </summary>
public event DataSourceChangedEventHandler DataSourceChanged;
/// <summary>
/// A readonly unique ID for the current view.
/// </summary>
public string CurrentViewID
{
get { return string.Concat("CurrentView_", UniqueID); }
}
/// <summary>
/// Overrides the data source property, so that we get a chance to test whether
/// the source
/// being bound contains data or not.
///
/// This is used to communicate with the pager so that for an empty list no
/// pager is shown.
/// </summary>
public override object DataSource
{
get
{
if (ViewState[CurrentViewID] != null)
return ViewState[CurrentViewID];
else
return base.DataSource;
}
set
{
base.DataSource = value;
ViewState[CurrentViewID] = value;
if (value == null)
{
if (DataSourceChanged != null)
DataSourceChanged(this, true);
}
else
{
if (DataSourceChanged != null)
DataSourceChanged(this, false);
}
}
}
/// <summary>
/// Smart Pager
/// </summary>
protected SmartPager pager;
/// <summary>
/// Numero of Page cliked
/// </summary>
public int PageNumberCliked
{
get { return (ViewState["PageNumberCliked"] == null) ? 1 : Int32.Parse(
ViewState["PageNumberCliked"].ToString()); }
set { ViewState["PageNumberCliked"] = value; }
}
/// <summary>
/// Display pager numeros grouped by Display value before '...' buton
/// </summary>
public int Display
{
get { return pager.Display; }
set { pager.Display = value; }
}
/// <summary>
/// Current first row to display
/// </summary>
public int rowB
{
get { return (Page.Session["rowB"] == null) ? 0 : Int32.Parse(
Page.Session["rowB"].ToString()); }
set { Page.Session["rowB"] = value; }
}
/// <summary>
/// Current end row to display
/// </summary>
public int rowE
{
get { return (Page.Session["rowE"] == null) ? PageSize : Int32.Parse(
Page.Session["rowE"].ToString()); }
set { Page.Session["rowE"] = value; }
}
/// <summary>
/// Calculate Page Count
/// </summary>
public int PageCount
{
get
{
if (RowCount == 0)
{
return PageSize;
}
if (this.DataSource == null)
{
throw new Exception("Datasource is empy");
}
if (PageSize == 0)
{
throw new Exception("Page size must be positive");
}
return (int)(RowCount / PageSize) + 1;
}
}
/// <summary>
/// Calculate Row Count
/// </summary>
public int RowCount
{
get { return (Page.Session["RowCount"] == null) ? 5 : Int32.Parse(
Page.Session["RowCount"].ToString()); }
set { Page.Session["RowCount"] = value; }
}
/// <summary>
/// Current Page
/// </summary>
public int CurrentPage
{
get { return (ViewState["CurrentPage"] == null) ? 1 : Int32.Parse(
ViewState["CurrentPage"].ToString()); }
set { ViewState["CurrentPage"] = value; }
}
/// <summary>
/// Constructor
/// </summary>
public GridViewPager() : base()
{
pager = new SmartPager();
pager.OnClickEvent += new OnClickPagerNumberEventHandler(pager_OnClickEvent);
}
#region events to implement on the page side
private static readonly object OnSelectRowEventKey = new object();
/// <summary>
/// This events must be implemented with a select statement to get rows
/// </summary>
public event EventHandler DoSelectRow
{
add { Events.AddHandler(OnSelectRowEventKey, value); }
remove { Events.RemoveHandler(OnSelectRowEventKey, value); }
}
protected virtual void OnSelectedRow(EventArgs e)
{
EventHandler handler = Events[OnSelectRowEventKey] as EventHandler;
if (handler != null)
handler(this, e);
else
throw new Exception("You must implement OnSelectRow method");
}
private static readonly object OnLoadRowCountEventKey = new object();
/// <summary>
/// This events must be implemented to know the row count
/// </summary>
public event EventHandler DoLoadRowCount
{
add { Events.AddHandler(OnLoadRowCountEventKey, value); }
remove { Events.RemoveHandler(OnLoadRowCountEventKey, value); }
}
protected virtual void OnLoadedRowCount(EventArgs e)
{
EventHandler handler = Events[OnLoadRowCountEventKey] as EventHandler;
if (handler != null)
handler(this, e);
else
throw new Exception("You must implement OnLoadRowCount method");
}
#endregion events to implement on the page side
#region Component event
/// <summary>
/// OnInit Event
/// </summary>
protected override void OnInit(EventArgs e)
{
base.OnInit(e); Page.Load += new EventHandler(Page_Load);
}
/// <summary>
/// PageLoad Event
/// </summary>
void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack) OnLoadedRowCount(EventArgs.Empty);
bool goodCall = true; //the current page is posted 2 times :
// 1- in javascript SmartPager.js by SmartPagerSelectPage when a new page
// is clicked (with callback parameter)
// 2- on the pager_OnClickEvent (without callback parameter)
foreach (string Key in Page.Request.Form.AllKeys)
{
if (Key.EndsWith("CALLBACKPARAM"))
{
goodCall = false;
}
}
//Handle just one time after pager_OnClickEvent
//call when the CALLBACKPARAM is missing
if (goodCall)
OnSelectedRow(EventArgs.Empty);
}
/// <summary>
/// Load the Control
/// </summary>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e); Controls.Add(pager);
}
/// <summary>
/// PreRender the Control
/// </summary>
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
pager.PageCount = PageCount;
}
/// <summary>
/// Event Handler Click Page with ICallBackEventHandler in SmartPager
/// </summary>
void pager_OnClickEvent(object sender, string pPageNum)
{
PageNumberCliked = Int32.Parse(pPageNum);
CurrentPage = PageNumberCliked;
if (CurrentPage == 1)
{
rowB = 0;
rowE = PageSize;
}
else
{
rowE = (PageSize * CurrentPage) - 1;
rowB = rowE - (PageSize - 1);
}
//Call postabck without call back parameters
Page.Response.Write( "<script language="Javascript">__" +
"doPostBack('__Page', 'MyCustomArgument');</script>" );
}
#endregion Component event
#region IStateManager Members
/// <summary>
/// Load the ViewState
/// </summary>
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
object[] state = savedState as object[];
if (state != null && state.Length == 2)
{
base.LoadViewState(state[0]);
if (state[1] != null)
((IStateManager)this.pager).LoadViewState(state[1]);
}
}
else
base.LoadViewState(savedState);
}
/// <summary>
/// Save the ViewState
/// </summary>
protected override object SaveViewState()
{
object[] state = new object[2];
state[0] = base.SaveViewState();
if (pager != null)
state[1] = ((IStateManager)this.pager).SaveViewState();
return state;
}
/// <summary>
/// Track the Viewstate
/// </summary>
protected override void TrackViewState()
{
base.TrackViewState();
if (pager != null)
((IStateManager)this.pager).TrackViewState();
}
#endregion
}
关注点
我学会了如何扩展像 GridView
这样复杂的控制。
历史
如果您有任何建议,我很乐意听取。