以上按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);
}
} 未完待续