public
class
DefaultPagerLayout:ITemplate

...
{
private
ImageButtonNext;
private
ImageButtonFirst;
private
ImageButtonLast;
private
ImageButtonPrevious;
private
PanelPager;

public
DefaultPagerLayout()

...
{
Next
=
new
ImageButton();
First
=
new
ImageButton();
Last
=
new
ImageButton();
Previous
=
new
ImageButton();
Pager
=
new
Panel();

Next.ID
=
"
Next
"
;Next.AlternateText
=
"
下一页
"
;Next.ImageUrl
=
"
play2.gif
"
;
First.ID
=
"
First
"
;First.AlternateText
=
"
首页
"
;First.ImageUrl
=
"
play2L_dis.gif
"
;
Last.ID
=
"
Last
"
;Last.AlternateText
=
"
末页
"
;Last.ImageUrl
=
"
play2_dis.gif
"
;
Previous.ID
=
"
Previous
"
;Previous.AlternateText
=
"
上一页
"
;Previous.ImageUrl
=
"
play2L.gif
"
;
Pager.ID
=
"
Pager
"
;
}
public
void
InstantiateIn(Controlcontrol)

...
{
control.Controls.Clear();
Tabletable
=
new
Table();
table.BorderWidth
=
Unit.Pixel(
0
);
table.CellSpacing
=
1
;
table.CellPadding
=
0
;
TableRowrow
=
new
TableRow();
row.VerticalAlign
=
VerticalAlign.Top;
table.Rows.Add(row);
TableCellcell
=
new
TableCell();
cell.HorizontalAlign
=
HorizontalAlign.Right;
cell.VerticalAlign
=
VerticalAlign.Middle;
cell.Controls.Add(First);
cell.Controls.Add(Previous);
row.Cells.Add(cell);
cell
=
new
TableCell();
cell.HorizontalAlign
=
HorizontalAlign.Center;
cell.Controls.Add(Pager);
row.Cells.Add(cell);
cell
=
new
TableCell();
cell.VerticalAlign
=
VerticalAlign.Middle;
cell.Controls.Add(Next);
cell.Controls.Add(Last);
row.Cells.Add(cell);

control.Controls.Add(table);
}
}
DefaultPagerLayout通过编程的方式提供了所有的导航元素,并将它们加入到aspx页面,不过这一次导航元素用标准的HTML表格设置了格式。现在,如果用户没有提供一个表现模板,程序将自动提供一个默认的模板。

[TemplateContainer(
typeof
(LayoutContainer))]
public
ITemplateLayout

...
{

get
...
{
return
(_layout
==
null
)
?
new
DefaultPagerLayout():_layout;}

set
...
{_layout
=
value;}
}
下面再来看看生成各个页面编号的过程。分页控件首先需要确定一些属性值,通过这些属性值来确定要生成多少不同的页面编号。
public
int
CurrentPage

...
{
get

...
{
string
cur
=
(
string
)ViewState[
"
CurrentPage
"
];
return
(cur
==
string
.Empty
||
cur
==
null
)
?
1
:
int
.Parse(cur);
}
set

...
{
ViewState[
"
CurrentPage
"
]
=
value.ToString();}
}

public
int
PagersToShow

...
{

get
...
{
return
_results;}

set
...
{_results
=
value;}
}

public
int
ResultsToShow

...
{

get
...
{
return
_resultsperpage;}

set
...
{_resultsperpage
=
value;}
}
CurrentPage保存的实际上是页面编号的ViewState中的当前页面,PagersToShow方法定义的属性允许用户指定要显示多少页面,而ResultsToShow定义的属性则允许用户指定每页要显示多少记录,默认值是10。
NumberofPagersToGenerate返回当前应当生成的页面编号的数量。
private
int
PagerSequence

...
{
get

...
{
return
Convert.ToInt32
(Math.Ceiling((
double
)CurrentPage
/
(
double
)PagersToShow));}
}

private
int
NumberOfPagersToGenerate

...
{

get
...
{
return
PagerSequence
*
PagersToShow;}
}

private
int
TotalPagesToShow

...
{

get
...
{
return
Convert.ToInt32(Math.Ceiling((
double
)TotalResults
/
(
double
)_resultsperpage));}
}
public
int
TotalResults

...
{

get
...
{
return
_builder.Adapter.TotalCount;}
}
TotalPagesToShow方法返回要显示的总页面数量,由用户预设的ResultsToShow属性调整。
虽然ASP.NET定义了一些默认的样式,不过对于分页控件的用户它们可能不是很实用。用户可以通过自定义样式来调整分页控件的外观。

public
StyleUnSelectedPagerStyle
...
{
get
...
{
return
UnselectedPager;}
}

public
StyleSelectedPagerStyle
...
{
get
...
{
return
SelectedPager;}
}
UnSelectedPagerStyle提供了页面编号未选中时所用的样式,而SelectedPagerStyle提供了页面编号被选中时所用的样式。
private
void
GeneratePagers(WebControlcontrol)

...
{
control.Controls.Clear();
int
pager
=
(PagerSequence
-
1
)
*
PagersToShow
+
1
;

for
(;pager<
=
NumberOfPagersToGenerate
&&
pager<
=
TotalPagesToShow;pager
++
)

...
{
LinkButtonlink
=
new
LinkButton();
link.Text
=
pager.ToString();
link.ID
=
pager.ToString();
link.Click
+=
new
EventHandler(
this
.Pager_Click);
if
(link.ID.Equals(CurrentPage.ToString()))
link.MergeStyle(SelectedPagerStyle);
else
link.MergeStyle(UnSelectedPagerStyle);

control.Controls.Add(link);
control.Controls.Add(
new
LiteralControl(
"
"
));
}
}

private
void
GeneratePagers()

...
{
GeneratePagers(Holder);
}
GeneratePagers方法动态地创建所有页面编号,页面编号是LinkButton类型的按钮。各个页面编号的标签和ID属性通过循环赋值,同 时,点击事件被绑定到适当的事件句柄。最后,页面编号被加入到一个容器控件——在本例中是一个Panel对象。按钮ID起到了标识哪一个按钮触发点击事件 的作用。下面是事件句柄的定义:
private
void
Pager_Click(
object
sender,System.EventArgse)

...
{
LinkButtonbutton
=
(LinkButton)sender;
CurrentPage
=
int
.Parse(button.ID);
RaiseEvent(PageChanged,
this
,
new
PageChangedEventArgs(CurrentPage,PagedEventInvoker.Pager));
Update();
}

private
void
Next_Click(
object
sender,System.Web.UI.ImageClickEventArgse)

...
{
if
(CurrentPage<TotalPagesToShow)
CurrentPage
++
;
RaiseEvent(PageChanged,
this
,
new
PageChangedEventArgs(CurrentPage,PagedEventInvoker.Next));
Update();
}

private
void
Previous_Click(
object
sender,System.Web.UI.ImageClickEventArgse)

...
{
if
(CurrentPage>
1
)
CurrentPage
--
;
RaiseEvent(PageChanged,
this
,
new
PageChangedEventArgs(CurrentPage,PagedEventInvoker.Previous));
Update();
}
private
void
First_Click(
object
sender,System.Web.UI.ImageClickEventArgse)

...
{
CurrentPage
=
1
;
RaiseEvent(PageChanged,
this
,
new
PageChangedEventArgs(CurrentPage,PagedEventInvoker.First));
Update();
}

private
void
Last_Click(
object
sender,System.Web.UI.ImageClickEventArgse)

...
{
CurrentPage
=
TotalPagesToShow;
RaiseEvent(PageChanged,
this
,
new
PageChangedEventArgs(CurrentPage,PagedEventInvoker.Last));
Update();
}
这些事件句柄都相似,它们首先更改分页控件的当前页面,然后刷新绑定的控件。
private
void
Update()

...
{
if
(
!
HasParentControlCalledDataBinding)
return
;
ApplyDataSensitivityRules();
BindParent();
BoundControl.DataBind();
}
首先,分页控件通过调用HasParentControlCalledDataBinding方法检查是否已经初始化了必要的适配器。如果是,则将前面 指出的根据数据显示情况自动调整控件的规则应用到当前的控件,这些规则使得分页控件根据BoundControl中数据的不同情况表现出不同的行为。虽然 这些规则由分页控件内部控制,但必要时可以用[GoF] State模式方便地移出到控件之外。
public
bool
IsDataSensitive

...
{

get
...
{
return
_isdatasensitive;}

set
...
{_isdatasensitive
=
value;}
}

private
bool
IsPagerVisible

...
{

get
...
{
return
(TotalPagesToShow
!=
1
)
&&
IsDataSensitive;}
}

private
bool
IsPreviousVisible

...
{
get

...
{
return
(
!
IsDataSensitive)
?
true
:
(CurrentPage
!=
1
);
}
}

private
bool
IsNextVisible

...
{
get

...
{
return
(
!
IsDataSensitive)
?
true
:
(CurrentPage
!=
TotalPagesToShow);
}
}

private
void
ApplyDataSensitivityRules()

...
{
FirstButton.Visible
=
IsPreviousVisible;
PreviousButton.Visible
=
IsPreviousVisible;
LastButton.Visible
=
IsNextVisible;
NextButton.Visible
=
IsNextVisible;
if
(IsPagerVisible)GeneratePagers();
}
ApplyDataSensitivityRules方法实施预定义的规则,诸如IsPagerVisible、IsPreviousVisible和 IsNextVisible。默认情况下,分页控件将启用这些规则,但用户可以通过设置IsDataSensitive属性来关闭这些规则。
至此为止,分页控件的显示部分基本设计完毕。最后剩下的结束工作是提供几个事件句柄,使得用户能够在各种分页控件事件出现时进行必要的调整。
public
delegate
void
PageDelegate(
object
sender,PageChangedEventArgse);


public
enum
PagedEventInvoker
...
{Next,Previous,First,Last,Pager}

public
class
PageChangedEventArgs:EventArgs

...
{
private
int
newpage;
private
Enuminvoker;

public
PageChangedEventArgs(
int
newpage):
base
()

...
{
this
.newpage
=
newpage;
}
public
PageChangedEventArgs(
int
newpage,PagedEventInvokerinvoker)

...
{
this
.newpage
=
newpage;
this
.invoker
=
invoker;
}

public
int
NewPage
...
{
get
...
{
return
newpage;}
}

public
EnumEventInvoker
...
{
get
...
{
return
invoker;}
}
}
由于分页控件需要返回自定义的事件参数,所以我们定义了一个专用的PageChangedEventArgs类。 PageChangedEventArgs类返回PagedEventInvoker类型,PagedEventInvoker类型是可能触发事件的控件 的枚举量。为了处理自定义的事件参数,我们定义了一个新的delegate,即PageDelegate。事件按照下面的形式定义:
public
event
PageDelegatePageChanged;
public
event
EventHandlerDataUpdate;
当事件没有对应的事件监听器时,ASP.NET会抛出一个异常。分页控件定义了下列RaiseEvent方法。
private
void
RaiseEvent(EventHandlere,
object
sender)

...
{
this
.RaiseEvent(e,
this
,
null
);
}

private
void
RaiseEvent(EventHandlere,
object
sender,PageChangedEventArgsargs)

...
{
if
(e
!=
null
)

...
{
e(sender,args);
}
}
private
void
RaiseEvent(PageDelegatee,
object
sender)

...
{
this
.RaiseEvent(e,
this
,
null
);
}

private
void
RaiseEvent(PageDelegatee,
object
sender,PageChangedEventArgsargs)

...
{
if
(e
!=
null
)

...
{
e(sender,args);
}
}
现在事件句柄可以通过调用各个RaiseEvent方法来触发事件了。
四、应用实例
至此为止,分页控件的设计已经全部完成,可以正式使用了。要使用该分页控件,只要把它绑定到一个表现控件即可。

<asp:RepeaterID="repeater"Runat="server">
<ItemTemplate>
列1:
<%#Convert.ToString(DataBinder.Eval(Container.DataItem,"Column1"))%>
<br>
列2:
<%#Convert.ToString(DataBinder.Eval(Container.DataItem,"Column2"))%>
<br>
列3:
<%#Convert.ToString(DataBinder.Eval(Container.DataItem,"Column3"))%>
<br>
<hr>
</ItemTemplate>
</asp:Repeater>
<cc1:Pagerid="pager"ResultsToShow="2"runat="server"BindToControl="repeater">
<SELECTEDPAGERSTYLEBackColor="Yellow"/>
</cc1:Pager>
上面的aspx页面将分页控件绑定到一个Repeater控件,设置每页显示的记录数量为2,选中的页面编号颜色为黄色,使用默认的布局,效果如图一。下面是另一个例子,它将分页控件绑定到一个DataGrid,效果如图二。

<
asp:DataGridID
=
"
Grid
"
Runat
=
"
server
"
></
asp:DataGrid
>
<
cc1:Pagerid
=
"
PagerGrid
"
ResultsToShow
=
"
2
"
runat
=
"
server
"
BindToControl
=
"
Grid
"
>
<
SELECTEDPAGERSTYLEBackColor
=
"
Red
"
></
SELECTEDPAGERSTYLE
>
<
LAYOUT
>
<
asp:ImageButtonid
=
"
First
"
Runat
=
"
server
"
imageUrl
=
"
play2L_dis.gif
"
AlternateText
=
"
首页
"
></
asp:ImageButton
>
<
asp:ImageButtonid
=
"
Previous
"
Runat
=
"
server
"
imageUrl
=
"
play2L.gif
"
AlternateText
=
"
上一页
"
></
asp:ImageButton
>
<
asp:ImageButtonid
=
"
Next
"
Runat
=
"
server
"
imageUrl
=
"
play2.gif
"
AlternateText
=
"
下一页
"
></
asp:ImageButton
>
<
asp:ImageButtonid
=
"
Last
"
Runat
=
"
server
"
imageUrl
=
"
play2_dis.gif
"
AlternateText
=
"
末页
"
></
asp:ImageButton
>
<
asp:Panelid
=
"
Pager
"
Runat
=
"
server
"
></
asp:Panel
>
</
LAYOUT
>
</
cc1:Pager
>
测试表明,分页控件并不依赖于特定的表现控件,它可以方便地处理不同的数据源,而且很容易使用,请读者下载本文后面的源代码参见完整的例子。
虽然学习开发自定义Web控件不是一件轻松的事情,但掌握这项技能带来的好处不言而喻,只要稍微增加一些工作量,开发者就可以将普通的Web控件改换成 多用途的通用控件,数十倍地提高工作效率,本文的分页控件只是创建通用控件来满足现有和将来表现需要的其中一个例子而已。
应用实例:


下载地址二:
http://download.****.net/source/334554
下载地址一:
http://files.cnblogs.com/downmoon/testGrid.rar
助人等于自助! [email protected]