以上按12個場景進行代碼分析,下面從技術點的角度來分析代碼:
技術點1:這個應用中,在表示層大量使用了一種自定義控件,繼承了Repeater類。先看看全文檢索寵物調用Simplepager的方式:
Part 1 頁面代碼,先引入標簽,后使用標簽。使用標簽時,先設置屬性,后設置模板。這個標簽可以設置頁面大小,當沒有條目時,給出提示,分頁時用了一個onpageindexchanged事件。
<PetsControl:simplepager id="products" runat="server" pagesize="4" emptytext="No products found." onpageindexchanged="PageChanged">
<headertemplate>
<table cellpadding="0" cellspacing="0">
<tr class="gridHead">
<td>Product ID</td>
<td>Name</td>
<td>Description</td>
</tr>
</headertemplate>
<itemtemplate>
<tr class="gridItem">

<td><%
# DataBinder.Eval(Container.DataItem, "Id") %></td>

<td><a href='Items.aspx?productId=<%# DataBinder.Eval(Container.DataItem, "Id") %>'><%
# DataBinder.Eval(Container.DataItem, "Name") %></a></td>

<td><%
# DataBinder.Eval(Container.DataItem, "Description") %></td>
</tr>
</itemtemplate>
<footertemplate>
</tbody>
</table>
</footertemplate>
</PetsControl:simplepager>
在
onpageindexchanged事件中,綁定數據源。

protected void PageChanged(object sender, DataGridPageChangedEventArgs e)
{
products.CurrentPageIndex = e.NewPageIndex;

// Get the search terms from the query string
string searchKey = WebComponents.CleanString.InputText(Request["keywords"], 100);


if (searchKey != "")
{

// Create a data cache key
string cacheKey = "search" + searchKey;

// Check if the objects are in the cache

if(Cache[cacheKey] != null)
{
products.DataSource = (IList)Cache[cacheKey];

}else
{
// If that data is not in the cache then use the business logic tier to fetch the data
Product product = new Product();
IList productsBySearch = product.GetProductsBySearch(searchKey);
// Store the results in a cache
Cache.Add(cacheKey, productsBySearch, null, DateTime.Now.AddHours(12), Cache.NoSlidingExpiration , CacheItemPriority.High, null);
products.DataSource = productsBySearch;
}

// Databind the data to the controls
products.DataBind();
}
}
Part 2 看看SimplePager的源代碼。在源代碼中,SimplePager繼承了Repeater,主要增添了分頁功能,大部分是屬性的定義,增加了OnPageIndexChanged事件,重寫了OnLoad、Render和OnDataBinding方法。Onload獲取請求參數,激發分頁事件。Render描述了如何渲染這個控件,OnDataBinding告訴控件要綁定哪些數據。

public class SimplePager : Repeater
{

//Static constants
protected const string HTML1 = "<table cellpadding=0 cellspacing=0><tr><td colspan=2>";
protected const string HTML2 = "</td></tr><tr class=gridNav><td>";
protected const string HTML3 = "</td><td align=right>";
protected const string HTML4 = "</td></tr></table>";
private static readonly Regex RX = new Regex(@"^&page=\d+", RegexOptions.Compiled);
private const string LINK_PREV = "<a href=?page={0}><img src=Images/buttonPrev.gif alt=Previous border=\"0\"></a>";
private const string LINK_MORE = "<a href=?page={0}><img src=Images/buttonMore.gif alt=More border=\"0\"></a>";
private const string KEY_PAGE = "page";
private const string COMMA = "?";
private const string AMP = "&";

protected string emptyText;
private IList dataSource;
private int pageSize = 10;
private int currentPageIndex;
private int itemCount;

override public object DataSource
{

set
{
//This try catch block is to avoid issues with the VS.NET designer
//The designer will try and bind a datasource which does not derive from ILIST

try
{
dataSource = (IList)value;
ItemCount = dataSource.Count;

}catch
{
dataSource = null;
ItemCount = 0;
}
}
}


public int PageSize
{

get
{ return pageSize; }

set
{ pageSize = value; }
}


protected int PageCount
{

get
{ return (ItemCount - 1) / pageSize; }
}


virtual protected int ItemCount
{

get
{ return itemCount; }

set
{ itemCount = value; }
}


virtual public int CurrentPageIndex
{

get
{ return currentPageIndex; }

set
{ currentPageIndex = value; }
}


public string EmptyText
{

set
{ emptyText = value; }
}


public void SetPage(int index)
{
OnPageIndexChanged(new DataGridPageChangedEventArgs(null, index));
}


override protected void OnLoad(EventArgs e)
{

if (Visible)
{
string page = Context.Request[KEY_PAGE];
int index = (page != null) ? int.Parse(page) : 0;
SetPage(index);
}
}



/**//// <summary>
/// Overriden method to control how the page is rendered
/// </summary>
/// <param name="writer"></param>

override protected void Render(HtmlTextWriter writer)
{
//Check there is some data attached

if (ItemCount == 0)
{
writer.Write(emptyText);
return;
}

//Mask the query
string query = Context.Request.Url.Query.Replace(COMMA, AMP);
query = RX.Replace(query, string.Empty);

// Write out the first part of the control, the table header
writer.Write(HTML1);
// Call the inherited method
base.Render(writer);

// Write out a table row closure
writer.Write(HTML2);
//Determin whether next and previous buttons are required
//Previous button?
if (currentPageIndex > 0)
writer.Write(string.Format(LINK_PREV, (currentPageIndex - 1) + query));

//Close the table data tag
writer.Write(HTML3);

//Next button?
if (currentPageIndex < PageCount)
writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query));

//Close the table
writer.Write(HTML4);
}


override protected void OnDataBinding(EventArgs e)
{

//Work out which items we want to render to the page
int start = CurrentPageIndex * pageSize;
int size = Math.Min(pageSize, ItemCount - start);
IList page = new ArrayList();

//Add the relevant items from the datasource
for (int i = 0; i < size; i++)
page.Add(dataSource[start + i]);
//set the base objects datasource
base.DataSource = page;
base.OnDataBinding(e);
}

public event DataGridPageChangedEventHandler PageIndexChanged;


virtual protected void OnPageIndexChanged(DataGridPageChangedEventArgs e)
{
if (PageIndexChanged != null)
PageIndexChanged(this, e);
}
}
Part 3 從上面的代碼你可以看出SimplePager有一個致命的缺點就是:如果一個頁面內有兩個SimplePager控件怎么辦?如果他們還依賴于請求的URL附帶參數的話,兩個控件豈不是會沖突?ViewStatePager正是為解決這個問題的,在ViewStatePager中用ViewState記住各自控件的記錄數和當前頁數。調用這個控件的地方是購物車頁面。

public class ViewStatePager : SimplePager
{

private const string KEY_ITEM_COUNT = "ItemCount";
private const string KEY_CURRENT_PAGE_INDEX = "CurrentPageIndex";
private const string IMG_PREV = "Images/buttonPrev.gif";
private const string IMG_MORE = "Images/buttonMore.gif";
private const string ALT_PREV = "Previous";
private const string ALT_MORE = "More";
private ImageButton btnPrev;
private ImageButton btnMore;


override protected int ItemCount
{

get
{ return (int)ViewState[KEY_ITEM_COUNT]; }

set
{ ViewState[KEY_ITEM_COUNT] = value; }
}


override public int CurrentPageIndex
{

get
{ return (int)ViewState[KEY_CURRENT_PAGE_INDEX]; }

set
{ ViewState[KEY_CURRENT_PAGE_INDEX] = value; }
}


override protected void OnLoad(EventArgs e)
{

if (!Page.IsPostBack && Visible)
{
CurrentPageIndex = 0;
SetPage(0);
}
}


private void PreviousClicked(object sender, ImageClickEventArgs e)
{
OnPageIndexChanged(new DataGridPageChangedEventArgs(sender, CurrentPageIndex - 1));
}


private void MoreClicked(object sender, ImageClickEventArgs e)
{
OnPageIndexChanged(new DataGridPageChangedEventArgs(sender, CurrentPageIndex + 1));
}


override protected void CreateControlHierarchy(bool useDataSource)
{
base.CreateControlHierarchy(useDataSource);

btnPrev = new ImageButton();
btnPrev.ImageUrl = IMG_PREV;
btnPrev.AlternateText = ALT_PREV;
btnPrev.Click += new ImageClickEventHandler(PreviousClicked);
Controls.Add(btnPrev);
btnMore = new ImageButton();
btnMore.ImageUrl = IMG_MORE;
btnMore.AlternateText = ALT_MORE;
btnMore.Click += new ImageClickEventHandler(MoreClicked);
Controls.Add(btnMore);
}


override protected void OnPreRender(EventArgs e)
{
btnPrev.Visible = CurrentPageIndex > 0;
btnMore.Visible = CurrentPageIndex < PageCount;
}


override protected void Render(HtmlTextWriter writer)
{

if (ItemCount == 0)
{
writer.Write(emptyText);
return;
}

writer.Write(HTML1);

for (int i = 0, j = Controls.Count - 2; i < j; i++)
Controls[i].RenderControl(writer);
writer.Write(HTML2);
btnPrev.RenderControl(writer);
writer.Write(HTML3);
btnMore.RenderControl(writer);
writer.Write(HTML4);
}
} 未完待續