使用IPostBackEventHandler让JavaScript“调用”回传事件

时间:2023-03-09 06:52:22
使用IPostBackEventHandler让JavaScript“调用”回传事件

由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 以启动回发事件。看看这个对象构造函数的几个参数

参数
targetControl
    类型:System.Web.UI.Control
         用于接收回发事件的 Control
argument
    类型:System.String

        在回发事件期间传递的可选参数。
actionUrl
    类型:System.String

        回发的目标。
autoPostBack
    类型:System.Boolean

        如果需要响应用户操作而自动将窗体回发到服务器,则为 true;否则为 false。
requiresJavaScriptProtocol
    类型:System.Boolean

        如果 javascript: 前缀是必需的,则为 true;否则为 false。
trackFocus
    类型:System.Boolean

        如果回发事件应将页返回到当前的滚动位置并将焦点返回到目标控件,则为 true;否则为 false。
clientSubmit
    类型:System.Boolean

         如果回发事件可以由客户端脚本引发,则为 true;否则为 false。
performValidation
    类型:System.Boolean

         如果在回发事件发生之前要求在客户端进行验证,则为 true;否则为 false。
validationGroup
    类型:System.String

         一个控件组,当该控件组回发到服务器时,PostBackOptions将引发对它的验证。

代码中 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效果类似,将字符串转为可执行语句,这时是不是既方便又可靠了呢。