(转)无刷新SELECT联动
无刷新SELECT联动
文:邓铭武
源地址:http://vision.anyp.com/uveditor/10184-229323.aspx
我们在做开发的过程中,多少都会遇到Select联动的问题。一般来讲,运用ASP.NET自带的DropDownList控件来做,是最方便的。如下:
Default.aspx |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>SELECT联动</title> </head> <body> <form id="form1" runat="server"> <asp:DropDownList ID="ddlProvinces" runat="server" AutoPostBack="True" OnSelectedIndexChanged="ddlProvinces_SelectedIndexChanged"> <asp:ListItem Value="">请选择</asp:ListItem> <asp:ListItem Value="0">广东省</asp:ListItem> <asp:ListItem Value="1">广西省</asp:ListItem> <asp:ListItem Value="2">山东省</asp:ListItem> <asp:ListItem Value="3">山西省</asp:ListItem> </asp:DropDownList> <asp:DropDownList ID="ddlCities" runat="server"> <asp:ListItem>请选择</asp:ListItem> </asp:DropDownList> </form> </body> </html>
|
Default.aspx.cs |
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) {
}
private string[][] propCities = { new string[]{"广州市", "茂名市"}, new string[]{"桂林市", "北海市"}, new string[]{"济南市", "胶南市"}, new string[]{"原平市", "高平市"} };
protected void ddlProvinces_SelectedIndexChanged(object sender, EventArgs e) { ddlCities.Items.Clear(); ddlCities.Items.Add(new ListItem("请选择", ""));
if (!String.IsNullOrEmpty(ddlProvinces.SelectedValue)) { int index = int.Parse(ddlProvinces.SelectedValue); string[] cities = propCities[index];
foreach (string text in cities) { ddlCities.Items.Add(new ListItem(text, text)); } } } } |
运用Asp.net的控件,我们只需要很少的代码就能完成这个任务。在每次点击的省份的时候,城市列表会跟着刷新,达到了数据联动的效果。
但问题结束了吗?还没有...
使用上面的代码,每次切换省份时候,整个页面都刷新一谝。这对于这个测试来讲不算得什么,但对大多数B/S应用来说,是很浪费资源的,用户体验也非常差。
那怎么办?
在当前大红大紫的Ajax技术冲击下,想必大家都会想到“局部刷新”的它来了。在asp.net下常用的Ajax解决方案有:
AjaxPro.net
Asp.net ajax 1.0
其中AjaxPro.net支持1.1版本与2.0;而Asp.net ajax 1.0则只是在Asp.net 2.0或以上版本才能使用。
那么我们也尝试使用Ajax技术来实现联动效果,但这里不打算使用框架的技术来实现,为以后应用的便利,把它封装成一个自定义控件,下面是控件的代码:
LoadSort.aspx |
<%@ Page Language="C#" Inherits="Laodeng.Common.DataDictionary.UI.LoadSortPage" %> |
LoadSort.aspx.cs |
using System; using System.Text;
namespace Laodeng.Common.DataDictionary.UI { public class LoadSortPaged : System.Web.UI.Page { private string[] propProvinces = { "广东省", "广西省", "山东省", "山西省" };
private string[][] propCities = { new string[]{"广州市", "茂名市"}, new string[]{"桂林市", "北海市"}, new string[]{"济南市", "胶南市"}, new string[]{"原平市", "高平市"} };
protected void Page_Load(object sender, EventArgs e) { String id = Request.QueryString["sid"];
String scripts = GetSortScript(id);
Response.Clear(); Response.Write(scripts); }
public String GetSortScript(string pid) { StringBuilder builder = new StringBuilder(1024); string[] cities = null; if (pid == "") { cities = propProvinces; } else { cities = propCities[int.Parse(pid)]; }
builder.AppendLine("var data = new Array();"); if (cities != null) { foreach (string city in cities) { builder.AppendLine(string.Format("data.push({{'name':'{0}','id':'{1}', 'isDefault':'{2}'}});", city, city, 0)); } }
return builder.ToString(); } } } |
这个页面负责为我们的控件提供数据,当Request.QueryString[“id”]为空时,返回省份列表,否则返回对应的城市列表。留意到,LoadSort.aspx只有<%@Page%>标记和LoadSort.aspx.cs的Response.Clear();作用都是,不让页面输出额外的垃圾代码。
SortDropDownList.cs |
using System; using System.Web.UI.HtmlControls; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls;
namespace Laodeng.Common.DataDictionary.UI { [ToolboxData("<{0}:SortDropDownList runat=server></{0}:SortDropDownList>")] public class SortDropDownList : WebControl { #region 属性列表 private string _LoadPage = "/LoadSort.aspx"; [Bindable(true)] [Category("Appearance")] [DefaultValue("/LoadSort.aspx")] [Localizable(true)] public string LoadPage { get { return _LoadPage; }
set { _LoadPage = value; } }
private Boolean _IsAutoLoad = true; [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public Boolean IsAutoLoad { get { return _IsAutoLoad; }
set { _IsAutoLoad = value; } }
private Boolean _IsAutoSelect = true; [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public Boolean IsAutoSelect { get { return _IsAutoSelect; }
set { _IsAutoSelect = value; } }
private Int32 _Timespace = 100; [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public Int32 Timespace { get { return _Timespace; }
set { _Timespace = value; } }
private String _DefaultText = "== 请选择=="; [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public String DefaultText { get { return _DefaultText; }
set { _DefaultText = value; } }
private String _DefaultValue = "[none]"; [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public String DefaultValue { get { return _DefaultValue; }
set { _DefaultValue = value; } }
private string _SelectedPath; [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public String SelectedPath { get { if (String.IsNullOrEmpty(Page.Request.Form[ID])) { return _SelectedPath; } else { return Page.Request.Form[ID]; } }
set { _SelectedPath = value; } }
private String _SelectedLastValue; [Bindable(false)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public String SelectedLastValue { get { if (_SelectedLastValue == null) { if (String.IsNullOrEmpty(SelectedPath)) { _SelectedLastValue = string.Empty; } else { _SelectedLastValue = SelectedPath.Split(',')[0]; } } return _SelectedLastValue; } }
#endregion
protected override void OnInit(EventArgs e) { base.OnInit(e);
Page.ClientScript.RegisterClientScriptResource(GetType(), "Laodeng.Common.DataDictionary.UI.sortdropdownlist.js"); }
protected override void RenderContents(HtmlTextWriter output) { output.AddAttribute("id", ID); output.RenderBeginTag(HtmlTextWriterTag.Div); output.RenderEndTag();
output.WriteLine("<script language=""javascript"" type=""text/javascript"">"); output.WriteLine(BuildScript()); output.WriteLine(String.Format("item_onchange(null, '', '{0}', '{1}');", ID, SelectedPath)); output.WriteLine("</script>"); }
#region 创建公共脚本 /// <summary> /// 创建公共脚本 /// </summary> /// <returns></returns> public String BuildScript() { StringBuilder builder = new StringBuilder(); builder.AppendLine(String.Format("isAutoLoad = {0};", IsAutoLoad.ToString().ToLower())); builder.AppendLine(String.Format("isAutoSelect = {0};", IsAutoSelect.ToString().ToLower())); builder.AppendLine(String.Format("space = {0};", this.Timespace)); builder.AppendLine(String.Format("defaultText = ""{0}"";", DefaultText)); builder.AppendLine(String.Format("defaultValue = ""{0}"";", DefaultValue)); builder.AppendLine(String.Format("loadPage = ""{0}"";", LoadPage)); return builder.ToString(); } #endregion } }
|
SortDropDownList.js |
//是否自动加载子数据 var isAutoLoad = true; //是否自动选择记录项 var isAutoSelect = true; //数据缓存 var cacheData = new Array(); //自动加载子数据的间隔 var space = 100; //默认项的文本 var defaultText = "== 请选择 =="; //默认项的值 var defaultValue = "[none]"; //加载数据的页面地址 var loadPage = "LoadSort.aspx";
function createItem(divId){ var newItem = document.createElement("SELECT"); newItem.name = divId; return newItem; } function removeChild(divBody, currentItem){ var watingBox = document.getElementById("watingBox"); if( watingBox != null ){ divBody.removeChild(watingBox); } if( divBody.childNodes.length > 0 ){ for( var i = divBody.childNodes.length - 1; i > 0; i-- ){ if( divBody.childNodes[i] != currentItem ){ divBody.removeChild(divBody.childNodes[i]); } else { break; } } } }
function addDefaultOption(currentItem){ if( defaultText == "" ) return;
var newOption = new Option(); newOption.value = defaultText; newOption.text = defaultText; currentItem.options.add(newOption); }
function appendSort(currentItem, data, divId, dValue){ var divBody = currentItem == null ? document.getElementById(divId) : currentItem.parentNode; var newItem = createItem(divId);
removeChild(divBody, currentItem);
if( data.length == 0 ) return;
var newOption = null;
addDefaultOption(newItem);
var selectedValue = null; for( var i = 0; i < data.length; i++ ){ newOption = new Option(); newOption.value = data[i].id; newOption.text = data[i].name; newItem.options.add(newOption);
if( data[i].isDefault == "True" && isAutoSelect ){ selectedValue = newOption.value; } }
if( dValue != null && dValue != "" ){ var index = dValue.indexOf(","); if( index > -1 ){ selectedValue = dValue.substring(0, index); dValue = dValue.substring(index + 1, dValue.length); } else { selectedValue = dValue; dValue = ""; } }
divBody.appendChild( newItem );
if( selectedValue != null ){ newItem.value = selectedValue;
if( isAutoLoad ){ setTimeout(function(){ item_onchange(newItem, selectedValue, divId, dValue); }, space); } }
newItem.onchange = function(){ item_onchange(this, this.value, divId, null); }; }
function showWatingBox(div){ var divBody = div; var watingBox = document.getElementById("watingBox"); if( watingBox == null ){ watingBox = document.createElement("SPAN"); watingBox.id = "watingBox"; watingBox.innerHTML = "加载中..."; divBody.appendChild(watingBox); } }
function item_onchange(element, superId, divId, dValue){ if( superId != defaultValue ){ if( cacheData[superId] == null ){ var url = loadPage + "?sid="+ superId +"&r=" + Math.random(); sendRequest(url, null, "GET", function(){ var ready = req.readyState; var dataScript = null; if( ready == 4 ){ dataScript = req.responseText;
eval( dataScript );
cacheData[superId] = data;
appendSort(element, data, divId, dValue); } else { showWatingBox( element == null ? document.getElementById(divId) : element.parentNode ); } }); } else { appendSort(element, cacheData[superId], divId, dValue); } } else { removeChild(element.parentNode, element); } if( element != null ){ window.focus(); } }
function getXMLHttpRequest(){ var request = null; if( window.XMLHttpRequest){ request = new XMLHttpRequest(); } else if( typeof(ActiveXObject) != "undefined" ){ request = new ActiveXObject("Microsoft.XMLHTTP"); } return request; }
var req; function sendRequest(url, params, httpMethod, method){ if( !httpMethod ){ httpMethod = "GET"; }
req = getXMLHttpRequest(); if( req ){ req.onreadystatechange = method; req.open(httpMethod, url, true); req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); req.send(params); } } |
SortDropDownList.cs是一个从WebControl继承的控件,它提供了SortDropDownList.js脚本的服务器端封装。SortDropDownList里面的代码为页面注册了脚本:
Page.ClientScript.RegisterClientScriptResource(GetType(), "Laodeng.Common.DataDictionary.UI.sortdropdownlist.js");
需要注意的是SortDropDownList.js文件的属性要如下设置
将脚本封装成控件,我们可以方便快捷的完成部署任务。运用Ajax技术、客户端数据缓存和加载提示效果,提供了良好的用户体验。
或许你看到这个例子只是省份/城市两级联动,如果要三级或发疯的无限级怎么办?
不用担心,只要你修改LoadSort.aspx让它能加载不同级别的数据,这个例子就能应付得绰绰有余。当拿到类似任务的时候,就准备好茶水,一边喝一边偷笑吧。