千里之行,始于足下

酌贪泉而觉爽,处涸辙而犹欢

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  34 随笔 :: 0 文章 :: 184 评论 :: 10 引用

  在使用 ASP.NET 的时候,我们仍然在许多情况下需要使用客户端脚本。以下是笔者根据自己的经验和一些粗浅的研究,对此作一个简要的总结。

  一、在 HTML 里直接写脚本

  这个方法是最简单的,直到如今我写网页的时候也几乎还是使用最多的一种方式。也许一些经常使用 RegisterClientScriptBlock 的人会觉得这种方法老土,不过在我看来,它除了可以减少编译时间以外,更主要的是可以减少代码量,可读性也要好一些,更或许还可以避免一些潜在的错误。

  但是有些情况下,直接写的方法是很难完成要求的,比如说当脚本需要依赖我们在代码中动态生成的控件时,就必须要采用 Register 的方法了。

  二、使用 Literal 控件写脚本

  Literal 控件基本上就是写一段文字或代码,所以当然也可以写出客户端脚本来,实质上这个方法与第一个方法基本相同。以我个人来说,此种方法一般是用在写控件上。设想一个情况:我们需要根据某种条件来判断一批具有客户端脚本事件的控件是否显示,当然我们可以使用如 Panel 一类的容器,可是这些容器多半都要套一个 div 之类的东西,如果在某种情况下我们不希望这时出现 div 呢?当然或许是有更好更漂亮的办法,只是我都是简单地用一个 Literal 了事。^_^

  三、使用 Register Script 函数

  在 ASP.NET 1.x 中,Page 类提供了 RegisterClientScriptBlock、RegisterOnSubmitStatement 以及 RegisterStartupScript 函数,使得可以在代码中进行 ClientScript 绑定。不过到了 ASP.NET 2.0 当中,将这些函数统通移到了一个 ClientScriptManager 类中,并且还添加了类似的 RegisterClientScriptInclude 函数。在使用这个类的时候,我们可以通过 Page.ClientScript 来访问。下面介绍一下这几个函数的区别。

  RegisterClientScriptBlock:这个函数将 ClientScript 加到页面顶部,一般是在紧接着 <form> 标签的地方。需要注意的是,这个函数中的脚本在写入页面甚至执行的时候,页面的其它元素可能还没有载入完毕,因此可能不能正确地调用页面中的元素。这个函数比较常见的应用是一些客户端事件处理函数。

  RegisterClientScriptInclude:这个函数是 ASP.NET 2.0 新加入的,它一般用于将一个外部的 ClientScript 文件,比如一个 .js 文件链接入页面的时候。除此之外,它和 RegisterClientScriptBlock 基本一样,包括注意事项。

  RegisterStartupScript:这个函数将 ClientScript 加入到页面的底部,由此带来的好处自然就是可以正确地处理对页面元素的引用了。注意这里的脚本将会在页面的 onload 事件之前执行。一般来说,这里的代码都是一次性执行的。最常见的比如说是希望页面载入完毕之后弹出一个消息提示框,又或者在一个有框架的页面中,需要在一个页面装载完毕之后更新另一个页面的情况。

  RegisterOnSubmitStatement:这个函数则是将 ClientScript 与 onsubmit 事件绑定起来。

  一般的,在使用 RegisterClientScriptBlock 定义了事件处理函数后,我们可以采用以下的代码将函数与控件关联起来,这里,假定我们已经定义了一个 confirmDelete 函数:

1string script = @"return confirmDelete();";
2btnDelete.Attributes.Add("onclick", script);

  四、关于 RegisterClientScriptResource

  除了前一节提过的四个函数外,ASP.NET 2.0 还增加了一个 RegisterClientScriptResource 函数。这个函数与前几个的差异在于:它联接的是经过编译成资源的脚本。比如如下一行

1Page.ClientScript.RegisterClientScriptResource(this.GetType(), "script_resource.js");

  这里的 script_resource.js 必须是在服务端被编译进 assembly 中去,其方法是在服务端代码中添加如下一行

1[assembly: WebResource("script_resource.js""application/x-javascript")]

  如此一来之后,在生成的页面中,我们可以看到类似下面的代码(为了节省版面,我删减了 d 和 t 的长度):

1<script src="/webclient/WebResource.axd?d=oZ35V30&amp;t=63204" type="text/javascript"></script>

  这里的 WebResource.axd 的请求被送到服务端之后,由一个特定的 axd HttpHandler 来处理以取得相关的资源(这里就是 script-resource.js 文件)。在后面我们还可以看到,这个技术还有着更广泛的应用。

  五、GetPostBackClientHyperlink 与 GetPostBackEventReference

  这两个函数都可以取得一个字符串,它可以用来作为客户端向服务端提交 PostBack 之用。总体上两者的用途是一样的,主要区别在于:前者返回的串以 "javascript:" 打头,而后者则没有。看以下的例子:

1ClientScriptManager cs = Page.ClientScript;
2btnDelete2.Attributes.Add("onclick", cs.GetPostBackEventReference(btnDelete, btnDelete2.ID.ToString());
3linkDelete.HRef = cs.GetPostBackClientHyperlink(btnDelete, linkDelete.ID.ToString());

  看看这一段代码生成的相关 HTML 源码:

1<input name="btnDelete2" type="button" id="btnDelete2" value="Delete2" onclick="__doPostBack('btnDelete','btnDelete2')" />
2<href="javascript:__doPostBack('btnDelete', 'linkDelete')" id="linkDelete">Delete</a>

  从这个例子我们可以看到两个函数的使用,对于超链接的 HRef 来说,应当采用 GetPostBackClientHyperlink,否则浏览器可能不能正确地执行。

  六、通过客户端触发服务端事件

  有一些情况下,我们需要通过客户端的事件来触发服务端事件。比如说,我们想在一个 TextBox 的 onchange 事件中加一个检测,如果遇上了用户输入回车,那么就触发一个 btnGo 的服务端事件。(当然实际上这个例子我们也可以完全用 javascript 办到,这里仅作说明)。能办到吗?以下是一段代码:

1string sCommand = Page.ClientScript.GetPostBackClientHyperlink(btnGo, "");
2string script = @"javascript:keyClick(""EVAL_COMMAND"")";
3script = script.Replace("EVAL_COMMAND", strCommand);
4txtSearch.Attributes.Add("onkeydown", script);

  以下的相关的 javascript 代码:

1function keyClick(cmd)
2{
3  if (event.keyCode == 13{
4    eval(cmd);
5  }

6}

  好,根据前面所讲的 GetPostBackClientHyperlink 的功能,我们很容易地推断出第一段代码所生成的 HTML 源码:

<input type="text" id="txtSearch" onkeydown="javascript:keyClick(&quot;javascript:__doPostBack('btnGo','')&quot;);" />

  可以看到在 txtSearch 的 onkeydown 处理函数 keyClick 中,通过 eval 函数再次调用了 btnGo 的服务端事件从而实现了由客户端事件触发 PostBack。是不是有些奇妙?

  七、ASP.NET 中的 Ajax?-- Client Callback

  Ajax 技术由于其无刷新的页面更新而使许多老式 Web 应用显得极为笨重。并且我们知道有不少 Ajax 其实内部就是 XmlHttpRequest 或是一个 xmlRequestFrame 来实现的,而这两个 IE 早就支持。那么微软有提供基于或是类似于 Ajax 的实现么?我知道最近出了一个 Atalas,但其实除此之外,在 ASP.NET 2.0 中就已经有了实现类似功能的办法了。

  首先,我们必须要使页面继承自 ICallbackEventHandler 这个接口。方法可以有如下两种,分别对应于 Code-Inside 和 Code-Behind 模式:

1<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>

1public partial class CallbackPage : System.Web.UI.ICallbackEventHandler 

  该接口是 ASP.NET 2.0 新加入的。接下来我们要实现它的两个成员方法(在我的本机 MSDN 里,有一些地方使用此接口的成员方法与实际成员不符,比如 RaiseCallbackEvent 的返回类型变成了 string 且没有 GetCallbackResult 方法,我估计是早期写好的但后来没有更新,大家看的时候要注意):

 1public int nCount = 0;
 2
 3public void RaiseCallbackEvent(String eventArgument)
 4{
 5  nCount = Convert.ToInt32(eventArgument) + 1;
 6}

 7
 8public string GetCallbackResult()
 9{
10  return nCount.ToString();
11}

  我们先写好接收回调函数的方法,用 javascript 写:

1function ReceiveServerData(rvalue, context)
2{
3  labelResult.innerText = rvalue;
4}

  好,随后我们需要将 Callback 链入页面,注意,关键部分到了:

1void Page_Load(object sender, EventArgs e)
2{
3  ClientScriptManager cs = Page.ClientScript;
4  String cbReference = cs.GetCallbackEventReference(this"arg""ReceiveServerData""context");
5  String callbackScript = "function CallServer(arg, context) {" + cbReference + ";}";
6  cs.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);
7}

  最后,还需要写好调用的地方:

1<input type="button" value="TestCallback" onclick="CallServer(value, alert('数据递增!'))" />

  这里的 value 是需要实现递增的数据。注意递增过程是在 RaiseCallbackEvent 函数内完成的,它就相当于 Ajax 中加上了 AjaxMethodAttribute 的函数。运行一下测试,我们可以发现确实也实现了类似 Ajax 的无刷新页面!

  看看生成的 HTML 源码,我们可以看到一行 javascript 脚本资源链接,也是 "WebResource.axd?" 后跟一串参数的一个请求,如同之前讲过的 RegisterClientScriptResource 生成的结果一样。此外还可以看到页面最后有一个用 RegisterStartupScript 生成的一段:

1<script type="text/javascript"><!--
2WebForm_InitCallback();// -->
3</script>

  而 CallServer 函数则被扩展成了类似下面的样子: 

1function CallServer(arg, context) 
2{
3  WebForm_DoCallback('__Page',arg,ReceiveServerData,"",null,false); 
4}

  这里的 WebForm_InitCallback 和 WebForm_DoCallback 显然是在 WebRequest.Axd 的请求所生成的 javascript 文件里,如果我们从 Temporary Internet Files 里打开它,可以看到这两个函数的实现,细细研究一番,能够发现它还是使用了 XmlHttpRequest 和 IFrame 来实现的。有兴趣的朋友们,去研究吧。

posted on 2006-04-22 23:32 sunwaywei 阅读(8565) 评论(18)  编辑 收藏 网摘 所属分类: 学习手记

评论

#1楼  2006-04-23 06:54 Dflying Chen      
谢谢,分析得很仔细!
  回复  引用  查看    

#2楼 [楼主] 2006-04-23 10:32 sunwaywei      
还有一个方法漏了,ASP.NET 2.0 中对于 Button 系列新增了一个 OnClientClick 属性,可用来生成客户端事件。示例:
 
1<asp:linkbutton id="LinkButton1" text="打开博客园" onclientclick="Navigate()"  runat=Server />
2    
3<script language=javascript>
4  function Navigate()
5  {
6    window.open("http://www.cnblogs.com");
7  }
    
8
</script>

假如这里的 javascript 我们已经写好,而 linkbutton 是我们需要在代码中动态生成,用这个办法是相当方便的。
  回复  引用  查看    

#3楼  2006-04-23 12:12 rc [未注册用户]
有问题
只要<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>,就会出现:“错误59class“_Default”必须为接口“System.Web.UI.ICallbackEventHandler”实现“Function GetCallbackResult() As String”。”等错误
  回复  引用    

#4楼 [楼主] 2006-04-23 13:06 sunwaywei      
@rc
这个错误我也发现过。当时我是 Copy & Paste 之后出现这个问题,后来我自己手工写就没再出现了。你试一试。
另外 Code-Behind 模式下,这个错误也会出现,解决方法相同。
  回复  引用  查看    

#5楼  2006-04-24 11:33 rc [未注册用户]
全部手工写,按回车后会自动生成这两个函数:
Public Function GetCallbackResult() As String Implements System.Web.UI.ICallbackEventHandler.GetCallbackResult

Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent

就没有问题,多谢 sunwaywei
  回复  引用    

#6楼 [楼主] 2006-04-24 11:55 sunwaywei      
@rc
不客气,共同进步
  回复  引用  查看    

#7楼  2006-04-24 18:30 rc [未注册用户]
七、ASP.NET 中的 Ajax?-- Client Callback中应该运行结果是每单击一下“TestCallback”按键,“labelResult.innerText ”中显示的值应该加1,为什么我就显示不出来呢?而且追踪public void RaiseCallbackEvent和public string GetCallbackResult()发现都没有运行


  回复  引用    

#8楼 [楼主] 2006-04-24 19:56 sunwaywei      
@rc
对不起,是我写的时候不严格。以下贴出我的全部代码:
 1<%@ Page Language="C#" %>
 2<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
 3
 4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 5
 6<script runat="server">
 7    public int nCount = 0;
 8
 9    public void RaiseCallbackEvent(String eventArgument)
10    {
11        nCount = Convert.ToInt32(eventArgument) + 1;
12    }

13
14    public string GetCallbackResult()
15    {
16        return nCount.ToString();
17    }

18
19    void Page_Load(object sender, EventArgs e)
20    {
21        ClientScriptManager cs = Page.ClientScript;
22        String cbReference = cs.GetCallbackEventReference(this"arg""ReceiveServerData""context");
23        String callbackScript = "function CallServer(arg, context) {" + cbReference + ";}";
24        cs.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);
25    }

26
</script>
27
28<html xmlns="http://www.w3.org/1999/xhtml" >
29<head runat="server">
30    <title>Untitled Page</title>
31</head>
32<script language="javascript">
33var svalue = 0;
34function ReceiveServerData(rvalue, context)
35{
36  labelResult.innerText = rvalue;
37  svalue = rvalue;
38}

39
</script>
40<body>
41    <form id="form1" runat="server">
42    <div>
43        <label id="labelResult">0</label>
44        <br />
45        <input type="button" value="TestCallback" onclick="CallServer(svalue, alert('数据递增!'))" />
46    </div>
47    </form>
48</body>
49</html>
50
 
以上代码经我测试通过,你可以参照一下。原先我写的地方,TestCallback 的 onclick 事件中,CallServer 函数不应当用 value 作参数名,因为它会与 value="TestCallback" 冲突,结果导致 "TestCallback" 被传到服务端,所以执行会不正确

  回复  引用  查看    

#9楼  2006-04-25 09:44 flyingchen [未注册用户]
我调试怎么老是报:错误 1 “_Default”不会实现接口成员“System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent(string)” c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\callbackclientscript\7a12d9dc\934b3787\App_Web_nielnz9y.0.cs

错误啊!求教
  回复  引用    

#10楼 [楼主] 2006-04-25 10:41 sunwaywei      
@flyingchen
如果你用 C#,请在 RaiseCallbackEvent 和 GetCallbackResult 函数前面加上 "ICallbackEventHandler.",再试一试。
如果你用 VB.NET,请按 rc 的方法。
  回复  引用  查看    

#11楼  2006-05-10 16:03 dudu      
谢谢!
看了您的文章,有不少收获。
  回复  引用  查看    

#12楼  2006-06-02 01:18 木鱼 [未注册用户]
因为想在速成版中用自定义事件,调试MSDN的程序,在.net 2003 中可以过,但在速成版中却过不去,请问是什么原因,有什么办法解决?
<%@ Page Language="C#" %>
<html>
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript">
var previousColor;
function MakeRed()
{
previousColor = window.event.srcElement.style.color;
window.event.srcElement.style.color = "#FF0000";
}
function RestoreColor()
{
window.event.srcElement.style.color = previousColor;
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:button id="Button1" runat="server"
text="Button1"
onmouseover="MakeRed();"onmouseout="RestoreColor();" />
</form>
</body>
</html>


  回复  引用    

#13楼 [楼主] 2006-06-02 08:36 sunwaywei      
@木鱼
就我看来,你在asp:Button之中直接定义onmouseover和onmouseout在.net 2.0中是不对的,因为它根本就不支持这两个属性,编译时即会报错。

我觉得,在你这个例子中,如果只是要实现颜色变换的效果,直接用最基本的input就可以。如:
<input type="button" id="Button1" value="Button1" onmouseover="MakeRed();" onmouseout="RestoreColor();" />

如果确实需要用asp:Button,那么你不妨在代码中使用类似 Button1.Attributes.Add("onmouseover", "MakeRed();") 这样的语法来为服务端控件加上客户端脚本事件。
  回复  引用  查看    

#14楼  2007-03-26 01:59 新动互联      
特价:5999元主机租用,100M独享只要15000元/年
配置:PD2.8 1024M内存 160GSATAII硬盘 最低10M带宽
买空间送域名,建网站送空间,买空间送企业邮箱,有ASP.NET PHP ASP虚拟主机空间 SQL2005/SQL2000数据库空间,

QQ:59716094
手机:13917868802
电话:021-64938114
MSN:ygxy2008@jx163.com
网址:http://www.newodong.com (新动互联)

  回复  引用  查看    

学习了,有不少收获。
  回复  引用  查看    

#16楼  2007-12-25 17:37 comm [未注册用户]
有个问题:
ClientScript.RegisterStartupScript(this.GetType(), "1", "alert('a');", true);
Response.Write(ClientScript.IsStartupScriptRegistered( "1"));

这里为什么会得到false,RegisterStartupScript是在什么时候注册到page的?

  回复  引用    

#17楼  2008-02-23 01:36 zx [未注册用户]
1<%@ Page Language="C#" %>
2<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
3
4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
6<script runat="server">
7 public int nCount = 0;
8
9 public void RaiseCallbackEvent(String eventArgument)
10 {
11 nCount = Convert.ToInt32(eventArgument) + 1;
12 }
13
14 public string GetCallbackResult()
15 {
16 return nCount.ToString();
17 }
18
19 void Page_Load(object sender, EventArgs e)
20 {
21 ClientScriptManager cs = Page.ClientScript;
22 String cbReference = cs.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context");
23 String callbackScript = "function CallServer(arg, context) {" + cbReference + ";}";
24 cs.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);
25 }
26</script>
27
28<html xmlns="http://www.w3.org/1999/xhtml" >
29<head runat="server">
30 <title>Untitled Page</title>
31</head>
32<script language="javascript">
33var svalue = 0;
34function ReceiveServerData(rvalue, context)
35{
36 labelResult.innerText = rvalue;
37 svalue = rvalue;
38}
39</script>
40<body>
41 <form id="form1" runat="server">
42 <div>
43 <label id="labelResult">0</label>
44 <br />
45 <input type="button" value="TestCallback" onclick="CallServer(svalue, alert('数据递增!'))" />
46 </div>
47 </form>
48</body>
49</html>
50
  回复  引用    





标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-04-22 23:44 编辑过
Google站内搜索

China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
近千种 9-95 新二手计算图书火热销售中!
开发者征途系统新作:《设计模式——基于C#的工程化实现及扩展》

相关文章:

相关链接: