一个下拉联动控件的实现
联动下拉菜单是我们平时做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 == 0) return "";
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}
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 == 0) return "";
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};
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}
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 == null) try { 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}
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 == null) try { 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的数组即可,因为没有放演示的空间 。。唉