IPostbackContainer 和 ICallbackContainer使得ASP.NET Server端控件可以集中处理由其内部子控件所引发的Postback 或者Callback过程。具体来讲是提供了这样一种机制,由子控件引发的Postback 或者Callback过程,在服务器端接到请求后,可以交由指定的父控件来处理请求。
实现IPostbackContainer 和 ICallbackContainer接口的控件也必须分别实现IPostbackEventHandler和 ICallbackEventHandler接口,否则无法处理由子控件引发的Postback 或者Callback过程。
.NET 标准控件中GridView, DetailsView 以及FormsView都实现了这两个接口,他们是这么做的哪,我们又如何在自定义的控件中实现这两个接口哪,请看下文:
1. IButtonControl 和 IPostbackOptions
IButtonControl接口:
1public interface IButtonControl
2{
3 string CommandArgument {get; set;}
4 string CommandName {get; set;}
5 string PostbackUrl {get; set;}
6
7 event EventHandler Click;
8 event CommandEventHandler Command;
9}
2{
3 string CommandArgument {get; set;}
4 string CommandName {get; set;}
5 string PostbackUrl {get; set;}
6
7 event EventHandler Click;
8 event CommandEventHandler Command;
9}
IButtonControl接口定义了Button类控件所必须具有的基本行为能力,比如当控件接收到用户点击时,能够主动进行回发,并且触发Click以及Command事件。
默认情况下IButtonControl所引发的Postback将被发送至当前页面,用户可以通过IButtonControl接口的PostbackUrl属性来指定回发页面从而实现跨Page的Postback。
.NET的标准控件Button, ImageButton以及LinkButton都实现了这个接口,而这三个控件是通过PostbackOptions这样一个数据包来配置生成其在客户端的Postback行为的。
PostbackOptions类型的成员
1public class PostbackOptions
2{
3 public string Argument {get; set;}
4 public Control TargetControl { get; }
5
6}
2{
3 public string Argument {get; set;}
4 public Control TargetControl { get; }
5
6}
其中Argument 属性用于指定回发过程中事件参数,而TargetControl 属性返回一个 Control 对象,该对象表示接收回发事件的控件。在IButtonControl引起回发时,这两个属性分别作为 EVENTARGUMENT 和EVENTTARGET 参数将传递给当前页面的JavaScript 方法_doPostBack来进行回发调用。
下来看一下Button控件的部分代码片断:
1public class Button : WebControl, IButtonControl, IPostBackEventHandler
2{
3 protected virtual PostBackOptions GetPostBackOptions(){}
4
5 protected override void AddAttributesToRender(HtmlTextWriter writer)
6 {
7
8 PostBackOptions postBackOptions = this.GetPostBackOptions();
9 string firstScript = string.Empty;
10
11
12 string postBackEventReference = this.Page.ClientScript.GetPostBackEventReference(postBackOptions, false);
13 if (postBackEventReference != null)
14 {
15 firstScript = Util.MergeScript(firstScript, postBackEventReference);
16 }
17
18
19 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, firstScript);
20
21
22 }
23
24}
从上面的代码中我们可以看出,Button控件会通过PostbackOptions这样一个数据包来配置其在客户端的点击行为。LunkButton和ImageButton也有类似的代码。2{
3 protected virtual PostBackOptions GetPostBackOptions(){}
4
5 protected override void AddAttributesToRender(HtmlTextWriter writer)
6 {
7
8 PostBackOptions postBackOptions = this.GetPostBackOptions();
9 string firstScript = string.Empty;
10
11
12 string postBackEventReference = this.Page.ClientScript.GetPostBackEventReference(postBackOptions, false);
13 if (postBackEventReference != null)
14 {
15 firstScript = Util.MergeScript(firstScript, postBackEventReference);
16 }
17
18
19 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, firstScript);
20
21
22 }
23
24}
2. IPostbackContainer
接口声明:
1public interface IPostbackContainer
2{
3 PostBackOptions GetPostBackOptions (IButtonControl buttonControl)
4}
这个接口比较简单,定义了一个方法来返回Postback设置。GridView控件正是使用这样一个接口来配置其内部的IButtonControl类子控件,例如Edit,Select按钮还有用来排序或者分页的LinkButton。2{
3 PostBackOptions GetPostBackOptions (IButtonControl buttonControl)
4}
下面是GridView的代码片断:
1public class GridView
2{
3
4 PostBackOptions IPostBackContainer.GetPostBackOptions(IButtonControl buttonControl)
5 {
6
7 PostBackOptions options = new PostBackOptions(this, buttonControl.CommandName + "$" + buttonControl.CommandArgument);
8 options.RequiresJavaScriptProtocol = true;
9 return options;
10 }
11}
2{
3
4 PostBackOptions IPostBackContainer.GetPostBackOptions(IButtonControl buttonControl)
5 {
6
7 PostBackOptions options = new PostBackOptions(this, buttonControl.CommandName + "$" + buttonControl.CommandArgument);
8 options.RequiresJavaScriptProtocol = true;
9 return options;
10 }
11}
可以看到GridView控件将PostbackOptions的TargetControl指向了自己,这样Postback被引发后,在Server端将由GridView来处理这次回发。
那么GridView内部的子控件是在什么时候来读取这样一个设置从而配置其客户端行为哪?
3. DataControlField
GridView控件的Columns属性代表了其所有的列信息,每一个Column都是一个DataControlFiel对象,该对象定义了在Runtime将会使用什么样的控件用来展示、编辑该列数据或者引发Postback以及Callback过程。
我们所关心的是CommandField和ButtonField,下面是CommandField类的代码片断:
1 private void AddButtonToCell(DataControlFieldCell cell, )
2 {
3 IButtonControl control;
4 IPostBackContainer container = base.Control as IPostBackContainer;
5
6 switch (this.ButtonType)
7 {
8 case ButtonType.Button:
9 control = new DataControlButton(container);
10 break;
11
12 case ButtonType.Link:
13 control = new DataControlLinkButton(container);
14 break;
15
16 default:
17 control = new DataControlImageButton(container);
18 break;
19 }
20
21 cell.Controls.Add((WebControl)control);
22 }
2 {
3 IButtonControl control;
4 IPostBackContainer container = base.Control as IPostBackContainer;
5
6 switch (this.ButtonType)
7 {
8 case ButtonType.Button:
9 control = new DataControlButton(container);
10 break;
11
12 case ButtonType.Link:
13 control = new DataControlLinkButton(container);
14 break;
15
16 default:
17 control = new DataControlImageButton(container);
18 break;
19 }
20
21 cell.Controls.Add((WebControl)control);
22 }
通过上面的代码,我们可以得知CommandField将会根据其ButtonType分别实例化DataControlButton或者DataControlLink或者DataControlImageButton控件,然后被添加到指定行上去。
而DataControlButton从Button控件派生, 当该控件在进行Render的时候,会到其PostbackContainer控件来读取Postback设置,然后Render到客户端去。
1internal sealed class DataControlButton : Button
2{
3 internal DataControlButton(IPostBackContainer container){}
4 protected sealed override PostBackOptions GetPostBackOptions()
5 {
6 if (this._container != null)
7 {
8 PostBackOptions postBackOptions = this._container.GetPostBackOptions(this);
9 if (this.Page != null)
10 {
11 postBackOptions.ClientSubmit = true;
12 }
13 return postBackOptions;
14 }
15 return base.GetPostBackOptions();
16
17 }
18}
2{
3 internal DataControlButton(IPostBackContainer container){}
4 protected sealed override PostBackOptions GetPostBackOptions()
5 {
6 if (this._container != null)
7 {
8 PostBackOptions postBackOptions = this._container.GetPostBackOptions(this);
9 if (this.Page != null)
10 {
11 postBackOptions.ClientSubmit = true;
12 }
13 return postBackOptions;
14 }
15 return base.GetPostBackOptions();
16
17 }
18}
4. 小结
基于IPostbackContainer接口可以使得ASP.NET Server端控件能够集中处理尤其子控件引发的Postback过程,我们在上面详细的剖析了GridView的整个实现过程,其核心在于当子控件进行Render的时候,回到其IPostbackContainer读取相关设置,更改触发本次Postback过程的Target Control,然后进行Render。
5. ICallbackContainer
ICallbackContainer的实现机制与IPostbackContainer类似。
全文完。
有什么不妥的地方,请大家指正。