button是用来接受用户在页面点击的。有些点击要把页面提交回服务端的。其实提交回服务端必然是基于类似这样的最简单的js: form1.submit()。问题是:服务端是如何知道页面上多个回传按钮中被点击的这一个呢?现在打算做一个最简单的button来看看:
public class button1:Control { protected override void Render(HtmlTextWriter writer) { writer.AddAttribute("type", "button"); writer.AddAttribute("value", this.UniqueID); writer.AddAttribute("id", this.UniqueID); writer.AddAttribute("onclick", Page.ClientScript.GetPostBackClientHyperlink(this, this.UniqueID)); writer.RenderBeginTag("input"); writer.RenderEndTag(); } }
在测试页面上把这个控件加上去。写如下代码:
protected void Page_Load(object sender, EventArgs e) { if (IsPostBack) { Response.Write(this.Request.Form["__EVENTARGUMENT"].ToString()); } }
在浏览器中浏览测试页面,点击后,页面会显示 Button1_1。在浏览器查看源代码,可看到相关的:
<input type="button" value="Button1_1" id="Button1_1" οnclick="javascript:__doPostBack('Button1_1','Button1_1')" /> <div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> </div> <script type="text/javascript"> <!-- var theForm = document.forms['form1']; if (!theForm) { theForm = document.form1; } function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } // --> </script>
这就是一句话:GetPostBackClientHyperlink 做到的。你需要注意页面上的两个隐藏域:__EVENTTARGET和__EVENTARGUMENT。
以上是客户端的提交机制。但asp.net的回发机制就不太清楚了:即一个<input type='submit' />引起的提交,与__EVENTTARGET和__EVENTARGUMENT是无关的。作为一个疑问吧。
接下来,继续完善这个控件,使点击它后做些处理。
protected void Page_Load(object sender, EventArgs e) { if (IsPostBack) { switch (Request.Form["__EVENTTARGET"]) { case "Button1_1": Button1_1_click(); break; } } }
void Button1_1_click() { Response.Write("Button1_1_click"); }
确实我们看到点击后的效果。但很显然页面代码很快就会很复杂了。条条道路通罗马,但要走好一点的路。如果想到用观察者模式,会使问题的逻辑清楚很多。你觉得一个页面与上面的一个按钮,应该是“按钮观察页面”还是“页面观察按钮”呢?我觉得是后者,就是说:页面是观察者,按钮是被观察者(可参考:http://www.cnblogs.com/zhenyulu/articles/73723.html一般来说:被观察者是观察者的成员)。使用委托和事件,我们可轻易的实现:
public delegate void myHandle();
public class button1:Control,IPostBackEventHandler { public event myHandle click; public event EventHandler click2; protected override void Render(HtmlTextWriter writer) { writer.AddAttribute("type", "button"); writer.AddAttribute("value", this.UniqueID); writer.AddAttribute("id", this.UniqueID); writer.AddAttribute("runat", "server"); writer.AddAttribute("onclick", Page.ClientScript.GetPostBackClientHyperlink(this, this.UniqueID)); writer.RenderBeginTag("input"); writer.RenderEndTag(); }
#region IPostBackEventHandler 成员
public void RaisePostBackEvent(string eventArgument) { if (click != null) { click(); }
if (click2 != null) { click2(this,null); } }
#endregion }
页面代码:
protected void Page_Load(object sender, EventArgs e) { Button1_1.click+=new mybutton.myHandle(Button1_1_click); Button1_1.click2 += new EventHandler(Button1_1_click2); }
void Button1_1_click2(object sender, EventArgs e) { Response.Write("Button1_1_click2"); }
protected void Button1_1_click() { Response.Write("Button1_1_click"); } 过程可描述为:页面注册按钮事件等候通知,按钮通知页面。myHandle不是常规的,只是让大家清楚机制与委托类型无关。接下来,所有的焦点集中在通知上。通知机制的关键在于IPostBackEventHandler。我们可以把页面代码加一个如下方法试一试:
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument) { Response.Write("www"); }
点击看看。为什么有且仅有www。把page的私有方法反射出来:
private void RaisePostBackEvent(NameValueCollection postData) { if (this._registeredControlThatRequireRaiseEvent != null) { this.RaisePostBackEvent(this._registeredControlThatRequireRaiseEvent, null); } else { string str = postData["__EVENTTARGET"]; bool flag = !string.IsNullOrEmpty(str); if (flag || (this.AutoPostBackControl != null)) { Control control = null; if (flag) { control = this.FindControl(str); } if ((control != null) && (control.PostBackEventHandler != null)) { string eventArgument = postData["__EVENTARGUMENT"]; this.RaisePostBackEvent(control.PostBackEventHandler, eventArgument); } } else { this.Validate(); } } }
清楚吗:
有:是因为(control != null) && (control.PostBackEventHandler != null) 仅有:是因为this.RaisePostBackEvent(control.PostBackEventHandler, eventArgument)被我们重载了。
最后,我们还有一个<input type="submit" /> 的疑问。朋友能帮忙解释吗?它的回发机制是怎样的?