在由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——实践篇(二)通过自己模拟HTML标签事件与服务器交互,讲了ASP.NET的服务器控件是怎么render成HTML后市怎么“调用”后台方法的,有同学看了后问了我个问题:你讲的方式确实可以,但我遇到的问题时这样的,我想让自己写的DIV点击一下提交表单,我是自己写post好呢还是用页面上的__doPostBack方法好呢?
我想了一下,觉得都不好。若是用自己写隐藏域,然后赋值提交的方法,原理虽然正确,但我们需要做很多额外工作;如果调用页面上自动生成的__doPostBack,万一页面上没有服务器控件,那么页面页面上也就不会有这个方法了,而且并不是所有的服务器控件都生成这个方法,退一万步,要是微软改接口,方法名字变了怎么办?
废话了半天,我们因该怎么处理这种情况呢?正如预期,微软又替我们想好了,IPostBackEventHandler接口就是做这事儿的,看个例子
<form id="form1" runat="server"> <div id="divTest" onclick="clientPostback(this);">Click to Post Back</div> <script type="text/javascript"> function clientPostback(obj) { setTimeout(postBackClientHandler.replace(/arg_placeholder/g,obj.id), 0); } </script> </form>
页面上有个DIV,点击的时候调用JavaScript clientPostBack方法,我们知道所谓JavaScript调用后台都是通过表单提交的方式实现的,在clientPostBack中有条奇怪的语句, setTimeout(postBackClientHandler.replace(/arg_placeholder/g,obj.id), 0); ,看看后台代码,就明白这是什么了
public partial class Default : System.Web.UI.Page,IPostBackEventHandler { protected override void OnPreRender(EventArgs e) { PostBackOptions pbo = new PostBackOptions(this, "arg_placeholder", "", false, false, false, true, false, ""); string clientScript = Page.ClientScript.GetPostBackEventReference(pbo); string postBackClientHandler = string.Format("\nvar postBackClientHandler=\"{0}\";\n", clientScript); Page.ClientScript.RegisterStartupScript(typeof(Default), "postBackClientHandler", postBackClientHandler, true); base.OnPreRender(e); } public void RaisePostBackEvent(string eventArgument) { Response.Write("Event argument is " + eventArgument); } }
首先让类实现IPostBackEventHandler接口,实现RaisePostBackEvent方法,MSDN上市这么解释这个方法的:当由类实现时,使服务器控件能够处理将窗体发送到服务器时引发的事件。这个方法就是JavaScript 提交表单后.NET自动执行的方法,我们把页面传来的参数输出。
在页面的PreRender事件处理程序中,使用PostBackOptions对象创建并注册客户端提交表单所用的脚本,想看明白这段代码,首先得了解PostBackOptions对象。MSDN上这么解释PostBackOptions:指定如何生成客户端 JavaScript 以启动回发事件。看看这个对象构造函数的几个参数
代码中 string clientScript = Page.ClientScript.GetPostBackEventReference(pbo); 是整个注册的核心,通过这条语句就可以获取注册到客户端的脚本语句,然后注册到页面。看看页面生成的代码
<script type="text/javascript"> //<![CDATA[ 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>
<script type="text/javascript"> //<![CDATA[ var postBackClientHandler="__doPostBack('__Page','arg_placeholder')"; //]]> </script>
通过代码分歧可以看出string clientScript = Page.ClientScript.GetPostBackEventReference(pbo); 语句得到的结果是 __doPostBack('__Page','arg_placeholder')。我们通过string postBackClientHandler = string.Format("\nvar postBackClientHandler=\"{0}\";\n", clientScript); 语句将其包装成一个字符串变量注册到页面,否则它是一个函数调用语句,页面会不停的被提交(有兴趣同学可以试试将其直接注册到页面)。
这样回头看看客户端DIV onclick方法是什么意思
postBackClientHandler是字符串”__doPostBack('__Page','arg_placeholder')”
postBackClientHandler.replace(/arg_placeholder/g,obj.id) 这条语句是把字符串中的”arg_placeholder”替换为我们希望传递给服务器的参数,也就是通过参数隐藏域提交到服务器的数据,这样我们写的客户端方法实际上是这样
function clientPostback(obj) { setTimeout("__doPostBack('__Page','divTest')", 0); }
至于setTimeout是因为我们得到是一个字符串而不是JavaScript语句,使用setTimeout和eval效果类似,将字符串转为可执行语句,这时是不是既方便又可靠了呢。