(转)无刷新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让它能加载不同级别的数据,这个例子就能应付得绰绰有余。当拿到类似任务的时候,就准备好茶水,一边喝一边偷笑吧。

posted @ 2009-01-19 11:48  nirvanalst  阅读(1207)  评论(1编辑  收藏  举报