服务器自定义开发二之客户端脚本回发


    上一篇文章介绍了一个复合控件,下面将介绍ListPicker自定义控件,ListPicker是一个较复杂的自定义控件。因为它需要和脚本代码进行交互。

ASP.NET 服务器控件可以发送两种客户端脚本:客户端脚本块 和客户端 HTML 属性
  客户端脚本块通常是用 JavaScript 编写的,其中通常包含在发生特定的客户端事件时执行的函数。客户端 HTML 属性提供将客户端事件与客户端脚本联系在一起的方法。例如,以下的 HTML 页面中包含了客户端脚本块,脚本块中包含了名为 doClick() 的函数。该页面同时还包含一个按钮(通过 <input> HTML 元素创建),这个按钮的 onclick 属性与 doClick() 函数绑定。也就是说,只要用户单击该按钮,就开始执行 doClick() 函数中的客户端代码。在本示例中,将显示一个弹出式对话框。 如下:
<html>
<body>
<form>
<script language="JavaScript">
<!--function doClick() {alert("You clicked me!");} -->
</script>

<input type="button" onclick="doClick()" value="Click Me!" />
</form>
</body>
</html>


   对于以上 HTML 页面中的客户端脚本,客户端脚本块包含在 HTML 注释(<!-- 和 -->)中。之所以这样,是因为如果不将脚本块放入 HTML 注释中,那些不能识别脚本的旧式浏览器就会显示 <script> 块的内容。
   在上面的 HTML 页面中,<input> 元素的 onclick 属性绑定到 doClick() 函数,因此在单击该按钮时将执行 doClick() 函数。
在使用客户端脚本时,只有两个 HTML 窗体元素(“按钮”(Button) 和“图像按钮”(ImageButton))引起窗体回发。如果自定义控件呈现不引起回发的 HTML 元素(如“文本框”(TextBox) 或“链接按钮”(LinkButton)),而您希望控件启动回发,则可以在 ASP.NET 中通过依靠客户端脚本(JScript、JavaScript)的事件结构进行编程来实现这一功能。
要启用这种回发机制, 启动回发必须在控件 Render 方法中添加的代码,示意如下。
protected override void Render(HtmlTextWriter output) {
                  output.Write("<a  id=\"" + this.UniqueID + "\" href=\"javascript:" + Page.GetPostBackEventReference(this) +"\">");
                  output.Write(" " + this.UniqueID + "</a>");
            }

   代码里GetPostBackEventReference 方法发出启动回发的客户端脚本,并且还提供对启动回发事件的控件的引用。如果控件要捕获回发事件(来自客户端的窗体提交),它必须实现 System.Web.UI.IPostBackEventHandler 接口。此接口向 ASP.NET 页框架发出信号,指出控件希望收到回发事件通知。RaisePostBackEvent 方法允许控件处理该事件和引发其他事件。
 通常我们可以使用GetPostBackEventReference传递参数,请看下面示意代码:

   

using System;
using System.Web;
using System.Web.UI;
using System.Collections;
using System.Collections.Specialized;
using System.Web.UI.WebControls;

namespace Ctrl {

    public class Ctrl : Control, IPostBackDataHandler, IPostBackEventHandler {

        private int _value = 0;

        public int Value { get { return _value; }        set {  _value = value;   }   }

        public bool LoadPostData(String postDataKey, NameValueCollection values) {
           _value = Int32.Parse(values[this.UniqueID]);
           return false;
        }

        public void RaisePostDataChangedEvent() {    }


        public void RaisePostBackEvent(String eventArgument) {
          
           if (eventArgument == "加") {   this.Value++;      }
           else {    this.Value--;      }
        }

        protected override void OnPreRender(EventArgs e) {     }

        protected override void Render(HtmlTextWriter output) {
           output.Write("<h3>值:<input name=" + this.UniqueID + " type=text value=" + this.Value + "> </h3>");
           output.Write("<input type=button value=加
OnClick=\"jscript:"+ Page.GetPostBackEventReference(this, "加")+ "\"> |");
           output.Write("<input type=button value=减
OnClick=\"jscript:"+ Page.GetPostBackEventReference(this, "减")+ "\">");
        }   
    }   
}

 当时用GetPostBackEventReference输出脚本时,它的两个参数分别是:this和加(减),其中this用于表示回发控件本省,当我们使用如下方式使用该自定义控件时
<%@ Register TagPrefix="Ctrl" Namespace="Ctrl" Assembly="Ctrl" %>

<html>
   <body>
      <form method="POST" action="NonComposition2.aspx" runat=server>
          <Ctrl:Ctrl id="MyControl" runat=server/>
      </form>
   </body>
</html>


如果浏览服务器发给客户端的代码,就如下:

<html>

   <body>

      <form name="_ctl0" method="POST" action="Ctrl.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwtMTAwOTQyOTI0Nzs7PomXs73t9xBOKEFKyYyTmD+75ZXL" />


          <h3>值:<input name=MyControl type=text value=0> </h3><input type=button value=加 OnClick="jscript:__doPostBack('MyControl','加')"> |<input type=button value=减 OnClick="jscript:__doPostBack('MyControl','减')">

     
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />

<script language="javascript" type="text/javascript">
<!--
 function __doPostBack(eventTarget, eventArgument) {
  var theform;
  if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1) {
   theform = document._ctl0;
  }
  else {
   theform = document.forms["_ctl0"];
  }
  theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
  theform.__EVENTARGUMENT.value = eventArgument;
  theform.submit();
 }
// -->
</script>
</form>

   </body>

</html>


本例中用粗体显示的元素(两个隐藏字段和客户端脚本方法)是由 ASP.NET 页框架添加的。隐藏窗体字段指示要发送到哪个服务器控件,并可以指定一个要传递的参数。客户端脚本方法用于设置隐藏字段,并将窗体提交给服务器.
所以,我们可以在Ctrl自定义控件里使用
  public void RaisePostBackEvent(String eventArgument) {
          
           if (eventArgument == "加") {   this.Value++;      }
           else {    this.Value--;      }
        }
的代码获取参数并进行判断。
 在ListPicker.cs源代码里还调用Page的RegisterHiddenField额外注册了两个隐藏元素,如下
   Page.RegisterHiddenField(SelectedHelperID, String.Join(",", SelectedItems ));
   Page.RegisterHiddenField(AllHelperID, String.Join(",", AllItems ));

那么为什么要注册这两个隐藏控件哪?
我们先通过一个简单的javascript代码进行说明,下面是一段较为简单的脚本控件,它用于同步用户的选项,如下:

<SCRIPT  LANGUAGE="javascript"> 

function  setcity()  { 

switch  (document.shengshi.shengID.value)  { 
case  '江苏'  : 
var  labels  =  new  Array("南京","无锡","苏州"); 
var  values  =  new  Array("nj","wx","sz"); 
break; 

case  '安徽'  : 
var  labels  =  new  Array("合肥","蚌埠","淮北"); 
var  values  =  new  Array("hf","bb","hb"); 
break 

//  清空市列表选择框的内容 

document.shengshi.shiID.options.length  =  0; 

//  从数组中添加内容 

for(var  i  =  0;  i  <labels.length;  i++)  { 

document.shengshi.shiID.add(document.createElement("OPTION")); 
document.shengshi.shiID.options[i].text=labels[i]; 
document.shengshi.shiID.options[i].value=values[i]; 

//  选择第一个选项 

document.shengshi.shiID.selectedIndex  =  0; 

</SCRIPT> 
</HEAD> 
<BODY> 
<FORM  NAME="shengshi"> 

省: 
<SELECT  NAME="shengID"  OnChange="setcity()"> 
<OPTION  value="江苏">江苏</OPTION> 
<OPTION  value="安徽"  SELECTED>安徽</OPTION> 
</SELECT> 

市: 

<SELECT  NAME="shiID"></SELECT> 
</FORM> 
<!--  执行初始化选择列表  --> 
<SCRIPT  LANGUAGE="javascript"> 
setcity(); 
</SCRIPT> 


运行这段代码,当你在左边下拉框选择不同的省份,相应的右边下拉框里显示相应省份里的市区,但是现在有一个问题,不管你新选择哪个省份或者市区,你查看它的源代码,它生成的HTML代码保持不变,所以如果将该数据提交到服务器后,你得到的输出结果永远都是空值。
   这是因为javascript是由浏览器内部运行机制实现的,并不体现在HTML代码上,所以为了解决这个问题,我们同样需要使用javascript保存用户的选项,后面读者可以看到ListPicker通过注册隐藏字段实现运行。这个是后面ListPicker实现的核心
 事实上,也是我们常用的DropDownList、ListBox等实现的核心方式。(未完)

posted @ 2005-12-04 10:20  启明星工作室  阅读(2798)  评论(0编辑  收藏  举报