桃李不言,下自成蹊;软件工程师,非某语言的程序员

一个下拉联动控件的实现

联动下拉菜单是我们平时做web开发时经常会遇到的一种情况。有多种实现的方式。比如数据一次加载在客户端联动或者回调填充。前段时间要给部门内部做个简单的培训用到相关的javascript和控件开发的时候,就那这个当作demo来做了。控件大概有4个部分组成,
1:控件的cs代码(AutoFillDDL.cs),2:客户端回应服务器段返回的数据,构建Option的js(AutoFill.js),3.服务器段写获取数据的方法的接口(IXBCallbackHandler)4。发送xmlHTTP请求的类的js(NetLoad.js)
AutoFillDDL.cs

  1using System;
  2using System.Collections.Generic;
  3using System.Text;
  4using System.Web.UI.WebControls;
  5using System.Web.UI;
  6using System.ComponentModel;
  7using Beyondbit.Soft3.WebControl.Designer;
  8
  9[assembly: WebResource("Beyondbit.Soft3.WebControl.Js.AutoFill.js""application/x-javascript")]
 10[assembly: WebResource("Beyondbit.Soft3.WebControl.Js.NetLoad.js""application/x-javascript")]
 11namespace Beyondbit.Soft3.WebControl
 12{
 13    public class AutoFillDDL : Label
 14    {
 15
 16        private string _controlfromrelation = "";
 17        [TypeConverter(typeof(AutoFillControlConvert)), IDReferenceProperty, Category("Behavior"), Themeable(false), DefaultValue(""), Description("需要联动的父的控件")]
 18        public string ControlFromRelation
 19        {
 20            get
 21            {
 22                return _controlfromrelation;
 23            }

 24            set
 25            {
 26                _controlfromrelation = value;
 27            }

 28        }

 29
 30        private string _controltorelation = "";
 31        [TypeConverter(typeof(AutoFillControlConvert)), IDReferenceProperty, Category("Behavior"), Themeable(false), DefaultValue(""), Description("需要联动的子的控件")]
 32        public string ControlToRelation
 33        {
 34            get
 35            {
 36                return _controltorelation;
 37            }

 38            set
 39            {
 40                _controltorelation = value;
 41            }

 42        }

 43
 44
 45        方法 
 89    
 90    
 91        private string XBGetCallbackResult(string eventArgument,string callbackId)
 92        {
 93            return GetJsonStringFromList(GetIXBCallBackReslut(eventArgument, callbackId));                       
 94        }
   
 95
 96    
 97        protected virtual List<ListItem> GetIXBCallBackReslut(string eventArgument, string callbackId)
 98        {
 99            string[] arrArg = eventArgument.Split('|');
100            if (arrArg.Length == 2)
101            {
102                ListItem list = new ListItem();
103                list.Text  = arrArg[0!= null && arrArg[0!=""  ? arrArg[0] : "" ;
104                list.Value = arrArg[1!= null && arrArg[1!= "" ? arrArg[1] : "";                
105                if (this is IXBCallbackHandler)  //控件内部实现了接口那么使用控件内部的方法,用于其他继承该控件的子控件
106                {
107                    return ((IXBCallbackHandler)this).GetIXBCallBackReslut(list, callbackId);
108                }

109                else if (this.Page is IXBCallbackHandler)  //页面实现了接口
110                {
111                    return ((IXBCallbackHandler)this.Page).GetIXBCallBackReslut(list, callbackId); ;
112                }

113                else //其他则提示
114                {
115                    List<ListItem> lists = new List<ListItem>();
116                    lists.Add(new ListItem("没有实现IXBCallbackHandler接口"""));
117                    return lists;
118                }

119            }

120            else //下拉表的值或者字中不能包含|字符,也可以用其他字符代替
121            {
122                List<ListItem> lists = new List<ListItem>();
123                lists.Add(new ListItem("发生意外错误,可能是你的名称或的值里面包含|"""));
124                return lists;
125            }

126
127        }
   
128        //拼接json字符串
129        protected internal static string GetJsonStringFromList(List<ListItem> list)
130        {
131            if (list.Count == 0return "";
132
133            StringBuilder sb = new StringBuilder() ;
134            sb.AppendLine("{options:[");
135            for(int i = 0 ,j =list.Count;i<j ;i++)
136            {
137                if( i !=0 ) sb.AppendLine(",") ;
138                sb.AppendLine("{");
139                sb.AppendLine("Text:\""+list[i].Text+"\",");           
140                sb.AppendLine("Value:\""+list[i].Value+"\"");
141                sb.AppendLine("}");
142            }

143            sb.AppendLine("]}");
144            return sb.ToString();
145        }
       
146    }

147}

AutoFill.js

 1//构造函数,用于声明该类 
 2function AutoFill(control,fromDll,toDll){
 3    this._control=control;  //联动控件的clientID       
 4    //获取联动的发起的下拉表对象
 5    this._fromddllobj=document.all?document.all[fromDll]:document.getElementById(fromDll);
 6    //响应联动的下拉表对象
 7    this._todllobj=document.all?document.all[toDll]:document.getElementById(toDll);
 8    //为解决闭包的问题,把AutoFill的实例付给下拉表对象的一个临时变量
 9    this._fromddllobj.af=this;  
10    //给下拉表附加onchange事件 ,原想使用AttachEvent的方法,
11    //但是这样的附加this的取值就会有问题 ,非下拉表本身而是window对象,
12    //不知道微软在实现上是否有问题,或者是我有问题?
13    this._fromddllobj.onchange = function (){  
14        //当onchange的时候 触发该函数
15        this.af.ajaxfun(this.af._control,this.options[this.selectedIndex].text+"|"+this.options[this.selectedIndex].value,this.af._todllobj); 
16    }
;    
17}
;
18AutoFill.prototype.ajaxfun=function (control,arg,childDll){    
19    //关于NetLoad,请参阅NetLoad.js
20    var ajax = new NetLoad();
21    var url=ajax.GetPesentUrl() ;   //获取发送的url地址
22    url +=url.indexOf("?")>0?"&":"?" ;
23    url +="Action=AutoFill_GetData&__CALLBACKID="+this.Encode(control)+"&__CALLBACKPARAM="+this.Encode(arg) ;
24    ajax.url =url ;
25    ajax.method="get";    
26    ajax.OpbackObj = childDll ;//回调函数中需要处理的下拉表,也就是  _todllobj对象
27    ajax.callback =this.buildToDDL ;//回调函数
28    ajax.send();    //发送请求
29}
;
30AutoFill.prototype.Encode = function(parameter)
31{
32    if (encodeURIComponent) {
33        return encodeURIComponent(parameter);
34    }

35    else {
36        return escape(parameter);
37    }

38}

39AutoFill.prototype.buildToDDL=function (ret,opObj){
40    if(!opObj){
41        alert("Error:\nPlease Check Your RP Then Try Again");
42        return ;
43    }
;
44    try{      
45        if(ret.responseText == null || ret.responseText ==""return ;        
46        //接受回发的json字符串,并付给该对象 ,可查看图片
47        var json=eval(ret.responseText);
48        opObj.options.length = 0;  //将下拉表的内容清空
49        //循环添加选项 
50        //如果 ,如果节点较多的话可以考虑先声明一个文档片段,然后把选项添加到文档片段
51        //最后将文档对象附加到下拉表
52        for(var i=0,j=json.length;i<j;i++){
53             var option = document.createElement("OPTION");
54             option.value =json[i].Value;
55             option.innerHTML = json[i].Text;     
56             opObj.appendChild(option);            
57        }
;
58        opObj.selectedIndex = 0 ; //默认选中第一个
59        //如果其onchange事件存在,那么触发这个事件,用于多级联动
60        //大家可以注意一下我这里的引用方式opObj["onchange"],效果同 opObj.onchange
61        if(opObj["onchange"!= null){
62            opObj.fireEvent("onchange"); 
63    }

64    }
;
65    catch(e){
66        throw e;
67        //alert(e.description);
68        return ;
69    }

70    ;
71    
72}
;
73AutoFill.prototype.callBackError=function (ret,opObj){
74    alert("Error:\n"+ret);//未实现    
75}
;

IXBCallbackHandler.cs

 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Web.UI.WebControls;
 5
 6namespace Beyondbit.Soft3.WebControl
 7{
 8    public interface IXBCallbackHandler
 9    {
10        List<ListItem> GetIXBCallBackReslut(ListItem parent,string callItemId) ;
11    }

12}

NetLoad.js

 1 /*
 2@author : xuanye(ilik)
 3@Createdate:2006-12-14
 4@des : xmlHttp请求类
 5*/

 6function NetLoad(smothod,surl,sasync,sOpbackObj,scallback) {
 7    this.xmlObj = this.CreateObjHttp();
 8    this.method=smothod?smothod:"POST";
 9    this.url = surl?surl:this.GetPesentUrl();
10    this.async=sasync?sasync:true;
11    this.OpbackObj=sOpbackObj?sOpbackObj:null;
12    this.contentXMLDom=null ;    
13    this.callback=scallback?scallback:function(cbobj,opobj) {return;}
14}

15NetLoad.prototype.GetPesentUrl = function()
16{
17    var __theForm = document.forms[0] ;
18    if(__theForm &&__theForm.action)
19    return __theForm.action ;
20    else
21    return window.location.href ;
22}

23NetLoad.prototype.send = function(context)
24{  
25    var netload = this ;    
26    if(!netload.method||!netload.url||!netload.async) return false;
27        netload.xmlObj.open (netload.method, netload.url, netload.async);
28        if(netload.method=="POST") netload.xmlObj.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
29        netload.xmlObj.onreadystatechange=function() {
30            if(netload.xmlObj.readyState==4{
31                if(netload.xmlObj.status==200{                   
32                    netload.callback(netload.xmlObj,netload.OpbackObj);
33                }

34            }

35        }

36        if(netload.method=="POST" && typeof(context) != "undifined" ){     
37            if(netload.contentXMLDom==null)
38                netload.contentXMLDom = netload.GetXmlDom();             
39            netload.contentXMLDom.loadXML(context);
40            netload.xmlObj.send(netload.contentXMLDom);
41        }

42        else {
43            netload.xmlObj.send(null);
44        }

45}

46NetLoad.prototype.GetXmlDom = function()
47{
48      var obj = null;
49      if (typeof(DOMParser) != "undefined"// Gecko、Mozilla、Firefox
50        var parser = new DOMParser();
51        obj = parser.parseFromString(xmlText, "text/xml");
52      }
 else // IE
53        try { obj = new ActiveXObject("MSXML2.DOMDocument");} catch (e) { }
54        if (obj == nulltry { obj = new ActiveXObject("Microsoft.XMLDOM"); } catch (e) { }
55      }

56      return obj;
57}

58NetLoad.prototype.CreateObjHttp = function()
59{
60    var xhttp = null;
61    if(window.XMLHttpRequest)
62    //IE7, Mozilla ,Firefox 
63      xhttp = new XMLHttpRequest();      
64    }

65    else if(window.ActiveXObject)
66    //IE6、IE5
67      try
68         xhttp = new ActiveXObject("Msxml2.XMLHTTP");
69      }
 
70      catch (e){ ; }      
71      if( xhttp == null)
72      {
73       try {
74            xhttp = new ActiveXObject("Microsoft.XMLHTTP");
75        }

76        catch (e){; }   
77      }
    
78    }

79    return xhttp;
80}

本来回调使用的是微软本身的ICallBack但是在实现多级关联的时候,即回调中又触发另外一次回调的时候js报错了,大概看了一下,应该是用了全局变量,在第二次回调的时候变量的值又被改变了。循环因子大于索引了。。 代码大部份已经给了详细的注释。。
汗,忘了讲怎么调用了,
编译成dll之后,在工具栏拖到页面上即可使用多级联动就拖多个,对应不同的父子关系
可以在使用的页面或者继承该控件的子控件中实现IXBCallbackHandler接口,

parent就是触发联动的下拉菜单的选中项的,callItemID就是联动控件的ClientID,在多级联动中可以根据这个判断这个值来判断是哪个回调,然后相应的返回各自的对应数据
取数据的方法是自己定义的返回一个ListItem的数组即可,因为没有放演示的空间 。。唉

posted @ 2007-02-07 08:26  假正经哥哥  阅读(2487)  评论(3编辑  收藏  举报