1. 客户端脚本触发回调方法
2. 服务器端代码响应回调,并更新数据控件,将更新后的数据控件内容(html编码)发回客户端
3. 客户端代码根据发回的内容重绘该数据控件
示例代码中,空白的页面上有三个DropDownListBox控件 DropDownListBox_A, DropDownListBox_B和 DropDownListBox_C,最终的目的是实现在页面不刷新的情况下当DropDownListBox_A的选择更改时动态的更新DropDownListBox_B和DropDownListBox_C的内容,实现联动。
在服务器端,首先要让自己的页面实现 ICallbackEventHandler接口,如:
public
partial
class
_Default : System.Web.UI.Page, ICallbackEventHandler
然后在页面上添加如下三个方法及一个公共string,固定套路:
回调三人组
#region 回调三人组
public string str_CallBack;
//引发回调事件处理
public void RaiseCallbackEvent(string the_id)
{
str_CallBack = the_id;
}
//回传回调结果
public string GetCallbackResult()
{
// 将前台传送来的参数分解
string[] parts = str_CallBack.Split('|');
// 构造参数队列
object[] theObject = new object[parts.Length - 1];
for (int int_index = 1; int_index < parts.Length; int_index++)
theObject[int_index - 1] = parts[int_index];
// 调用Page Method
return (string)GetType().GetMethod(parts[0]).Invoke(this, theObject);
}
private string RenderControl(Control control)
{
StringWriter writer1 = new StringWriter(CultureInfo.InvariantCulture);
HtmlTextWriter writer2 = new HtmlTextWriter(writer1);
control.RenderControl(writer2);
writer2.Flush();
writer2.Close();
return writer1.ToString();
}
#endregion
public string str_CallBack;
//引发回调事件处理
public void RaiseCallbackEvent(string the_id)
{
str_CallBack = the_id;
}
//回传回调结果
public string GetCallbackResult()
{
// 将前台传送来的参数分解
string[] parts = str_CallBack.Split('|');
// 构造参数队列
object[] theObject = new object[parts.Length - 1];
for (int int_index = 1; int_index < parts.Length; int_index++)
theObject[int_index - 1] = parts[int_index];
// 调用Page Method
return (string)GetType().GetMethod(parts[0]).Invoke(this, theObject);
}
private string RenderControl(Control control)
{
StringWriter writer1 = new StringWriter(CultureInfo.InvariantCulture);
HtmlTextWriter writer2 = new HtmlTextWriter(writer1);
control.RenderControl(writer2);
writer2.Flush();
writer2.Close();
return writer1.ToString();
}
#endregion
然后声明一个更新数据控件的方法,比如示例中的
1
public
string
BindDropDownList_B(
string
str_index)
2 {
3 // 简单绑定
4 DropDownList_B.Items.Clear();
5 for (int i = 0; i < 20; i++)
6 {
7 ListItem newItem = new ListItem();
8 newItem.Text = string.Format("{0} - B{1}", str_index, i.ToString());
9 DropDownList_B.Items.Add(newItem);
10 }
11
12 return RenderControl(DropDownList_B);
13}
14
15 public string BindDropDownList_C( string str_index)
16 {
17 DropDownList_C.Items.Clear();
18 for (int i = 0; i < 30; i++)
19 {
20 ListItem newItem = new ListItem();
21 newItem.Text = string.Format("{0} - C{1}", str_index, i.ToString());
22 DropDownList_C.Items.Add(newItem);
23 }
24
25 return RenderControl(DropDownList_C);
26}
2 {
3 // 简单绑定
4 DropDownList_B.Items.Clear();
5 for (int i = 0; i < 20; i++)
6 {
7 ListItem newItem = new ListItem();
8 newItem.Text = string.Format("{0} - B{1}", str_index, i.ToString());
9 DropDownList_B.Items.Add(newItem);
10 }
11
12 return RenderControl(DropDownList_B);
13}
14
15 public string BindDropDownList_C( string str_index)
16 {
17 DropDownList_C.Items.Clear();
18 for (int i = 0; i < 30; i++)
19 {
20 ListItem newItem = new ListItem();
21 newItem.Text = string.Format("{0} - C{1}", str_index, i.ToString());
22 DropDownList_C.Items.Add(newItem);
23 }
24
25 return RenderControl(DropDownList_C);
26}
在客户端,你需要将两个数据控件的Html编码用<span></span>包起来,id分别为span_b和span_c,并在header中声明如下脚本:
1
<
script language
=
"
javascript
"
type
=
"
text/javascript
"
>
2 // DropDownList_A的change
3 function OnChanged()
4 {
5 var context = span_b;
6 var theControl = document.getElementById("DropDownList_A");
7 // 调用服务器方法BindDropDownList_B,并将A当前选择的index传过去
8 var arg = "BindDropDownList_B|" + theControl.selectedIndex;
9
10 <%= ClientScript.GetCallbackEventReference(this, "arg", "UpdataDropDownList_B", "context")%>;
11}
12
13 function UpdataDropDownList_B(result, context)
14 {
15 // 重画控件
16 context.innerHTML = result;
17 // 避免同时更新失败
18 setTimeout("elsefunction()", 1);
19}
20
21 function elsefunction()
22 {
23 var context = span_c;
24 var theControl = document.getElementById("DropDownList_A");
25 var arg = "BindDropDownList_C|" + theControl.selectedIndex;
26
27 <%= ClientScript.GetCallbackEventReference(this, "arg", "UpdateDropDownList_C", "context")%>;
28}
29
30 function UpdateDropDownList_C(result, context)
31 {
32 context.innerHTML = result;
33}
34 </ script >
2 // DropDownList_A的change
3 function OnChanged()
4 {
5 var context = span_b;
6 var theControl = document.getElementById("DropDownList_A");
7 // 调用服务器方法BindDropDownList_B,并将A当前选择的index传过去
8 var arg = "BindDropDownList_B|" + theControl.selectedIndex;
9
10 <%= ClientScript.GetCallbackEventReference(this, "arg", "UpdataDropDownList_B", "context")%>;
11}
12
13 function UpdataDropDownList_B(result, context)
14 {
15 // 重画控件
16 context.innerHTML = result;
17 // 避免同时更新失败
18 setTimeout("elsefunction()", 1);
19}
20
21 function elsefunction()
22 {
23 var context = span_c;
24 var theControl = document.getElementById("DropDownList_A");
25 var arg = "BindDropDownList_C|" + theControl.selectedIndex;
26
27 <%= ClientScript.GetCallbackEventReference(this, "arg", "UpdateDropDownList_C", "context")%>;
28}
29
30 function UpdateDropDownList_C(result, context)
31 {
32 context.innerHTML = result;
33}
34 </ script >
最后在Page_Load中为DropDownList_A添加onchange属性
DropDownList_A.Attributes.Add(
"
onchange
"
,
"
OnChanged()
"
);
直接点运行吧,注意看IE的进度条。是不是无刷新实现了下拉列表的联动:)
需要说明的是,如果在Javascript脚本的OnChanged中接连两次回调服务器方法的话只有最后一次奏效,并且会有JavaScript报错,原因是微软的客户端回调实现代码中有一个缺陷:无论客户端回调了多少次,只要有一次回调完成,则视所有回调均完成,不是线程安全的!所以上述代码中使用了setTimeout做了中转,具体原因详见http://developers.de/files/279/download.aspx.
以上代码虽很好的完成了无刷新前提下的数据控件更新,但仍有一个问题至今没有解决,就是虽然控件的数据重新更新了,但是状态却还是最后一次PostBack时的状态(本例中为初始状态),如果你去取某个下拉框的值,仍就是初始值,不知道哪位朋友有这方面的解决办法,万分感谢!