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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

AutoFill.js
1
//构造函数,用于声明该类
2
function 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
};
18
AutoFill.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
};
30
AutoFill.prototype.Encode = function(parameter)
31
{
32
if (encodeURIComponent) {
33
return encodeURIComponent(parameter);
34
}
35
else {
36
return escape(parameter);
37
}
38
}
39
AutoFill.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
};
73
AutoFill.prototype.callBackError=function (ret,opObj){
74
alert("Error:\n"+ret);//未实现
75
};

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

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

2

3

4

5

6

7

8

9

10

11

12

NetLoad.js
1
/*
2
@author : xuanye(ilik)
3
@Createdate:2006-12-14
4
@des : xmlHttp请求类
5
*/
6
function 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
}
15
NetLoad.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
}
23
NetLoad.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
}
46
NetLoad.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
}
58
NetLoad.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

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

本来回调使用的是微软本身的ICallBack但是在实现多级关联的时候,即回调中又触发另外一次回调的时候js报错了,大概看了一下,应该是用了全局变量,在第二次回调的时候变量的值又被改变了。循环因子大于索引了。。 代码大部份已经给了详细的注释。。
汗,忘了讲怎么调用了,
编译成dll之后,在工具栏拖到页面上即可使用多级联动就拖多个,对应不同的父子关系
可以在使用的页面或者继承该控件的子控件中实现IXBCallbackHandler接口,
parent就是触发联动的下拉菜单的选中项的,callItemID就是联动控件的ClientID,在多级联动中可以根据这个判断这个值来判断是哪个回调,然后相应的返回各自的对应数据
取数据的方法是自己定义的返回一个ListItem的数组即可,因为没有放演示的空间 。。唉
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构