public class CustomGrid : Repeater… //Static constants protected const string HTML1 = "<table cellpadding=0 cellspacing=0><tr><td colspan=2>"; protected const string HTML2 = "</td></tr><tr><td class=paging align=left>"; protected const string HTML3 = "</td><td align=right class=paging>"; 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}>< Previous</a>"; private const string LINK_MORE = "<a href=?page={0}>More ></a>"; private const string KEY_PAGE = "page"; private const string COMMA = "?"; private const string AMP = "&"; 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); }
由于CustomGrid继承自Repeater控件,因而它同时还继承了Repeater的DataSource属性,这是一个虚属性,它默认的set访问器属性如下:
public virtual object DataSource { get {… } set { if (((value != null) && !(value is IListSource)) && !(value is IEnumerable)) { throw new ArgumentException(SR.GetString("Invalid_DataSource_Type", new object[] { this.ID })); } this.dataSource = value; this.OnDataPropertyChanged(); } }
对于CustomGrid而言,DataSource属性有着不同的设置行为,因而在定义CustomGrid控件的时候,需要改写DataSource虚属性,如下所示:
private IList dataSource; 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; } } }
当设置的value对象值不为IList类型时,set访问器就将捕获异常,然后将dataSource字段设置为null。
由于我们改写了DataSource属性,因而改写Repeater类的OnDataBinding()方法也就势在必行。此外,CustomGrid还提供了分页的功能,我们也需要实现分页的相关操作。与DataSource属性不同,Repeater类的OnDataBinding()方法实际上是继承和改写了Control基类的OnDataBinding()虚方法,而我们又在此基础上改写了Repeater类的OnDataBinding()方法:
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); }
此外,CustomGrid控件类还增加了许多属于自己的属性和方法,例如PageSize、PageCount属性以及SetPage()方法等。正是因为ASP.NET控件引入了Composite模式与Template Method模式,当我们在自定义控件时,就可以通过继承与改写的方式来完成控件的设计。自定义ASP.NET控件一方面可以根据系统的需求实现特定的功能,也能够最大限度地实现对象的重用,既可以减少编码量,同时也有利于未来对程序的扩展与修改。