之前的“省市县镇”四级控件是使用 MagicAjax 开发的。每一级都是到数据库中读取。经过 DBA 分析, 和该控件有关的表查询量很大, SQL 语句解析次数也非常多,虽然是 Oracle 数据库还是要尽量避免的。 为此新的控件将“省市”两级的数据存放在 JS 里,而“县镇”两级从数据库中读取,同时再结合项目需 要的特殊功能。
主要功能: 用户访问网站,首先根据二级域名加载用户所在的地区(如 http://xm.XXXX.com 福建-厦门)。如果没 有二级域名,那么根据 Cookie (如果之前访问过)加载用户所在的地区。如果没有,最后根据 IP 来 加载用户所在的地区。 如果是通过 Cookie 或是 IP 来判断,最后还要进行重定向(就是将 http://www.XXXX.com 转成 http://bj.XXXX.com )。如果是根据 IP 判断的,还要把用户所在的地区存入 Cookie。方便下次使用。 为了能从数据库读取数据,直接加载相应地区,还有能从下拉框中读取用户选择的地区就自定义一些属性。
特殊功能 是否重定向、是否只显示“省市”两级、是否垂直排列下拉框(默认是横向)、是否在在省份下拉框前 显示“省份”,城市下拉框前显示“城市”......、是否禁用控件(有些情况下,你可能要禁止用户选 择,等满足条件后才能选)
其他说明 前台脚本采用 JavaScript 和 DOM 互操作,全部使用 W3C 标准使其在各种浏览器下都正常。操作 <select> 采用更通用的 createElement() 而没有使用 options.add()。 后台采用 ASP.NET 进行少量操作(保存 Cookie||读数据库) 前后台采用 AjaxPro 使 JavaScript 和 C# 交互(传递枚举||调用方法||传递结果集)这部分应该是 重点....脚本和高级语言的交互是很让人向往的...暂时只会第三方 Ajax 最后采用一些看似多余的调用,实际上是为了防止 AjaxPro 生成的代码泄露了程序集和命名空间,老 大要求的...
顺便说下,如果数据库设计合理,前两位省份,中间两位城市,最后两位县/区,那么程序中很多地方 的代码都能省了...可见良好的数据库设计是非常重要的。
项目请一定要采用 AjaxPro 6.10.4.1 版本,其他版本有 BUG ,我也是开发中被搞乱了 下面就是源码了,没什么好说了...注释都很详细了 首先是 AreaInfo.cs 地区信息实体类,方便在其他程序读取用的
namespace
AreaDropDownList
...
{ /**/ /// <summary> /// 地区信息类 /// </summary> public sealed class AreaInfo ... { private int _SFID; private int _CSID; private int _XianID; private int _ZhenID; private string _SF; private string _CS; private string _Xian; private string _Zhen; private string _Domain; /**/ /// <summary> /// 省份ID /// </summary> public int SFID ... { get ... { return this ._SFID; } set ... { this ._SFID = value; } } /**/ /// <summary> /// 城市ID /// </summary> public int CSID ... { get ... { return this ._CSID; } set ... { this ._CSID = value; } } /**/ /// <summary> /// 县/区ID /// </summary> public int XianID ... { get ... { return this ._XianID; } set ... { this ._XianID = value; } } /**/ /// <summary> /// 镇 /// </summary> public int ZhenID ... { get ... { return this ._ZhenID; } set ... { this ._ZhenID = value; } } /**/ /// <summary> /// 省份名称 /// </summary> public string SF ... { get ... { return this ._SF; } set ... { this ._SF = value; } } /**/ /// <summary> /// 城市名称 /// </summary> public string CS ... { get ... { return this ._CS; } set ... { this ._CS = value; } } /**/ /// <summary> /// 县/区名称 /// </summary> public string Xian ... { get ... { return this ._Xian; } set ... { this ._Xian = value; } } /**/ /// <summary> /// 镇名称 /// </summary> public string Zhen ... { get ... { return this ._Zhen; } set ... { this ._Zhen = value; } } /**/ /// <summary> /// 域名 /// </summary> public string Domain ... { get ... { return this ._Domain; } set ... { this ._Domain = value; } } } }
接下来是数据访问层,项目使用的是 Oracle
using
System;
using
System.Data;
using
System.Data.OracleClient;
using
System.Web;
namespace
AreaDropDownList
...
{ /**/ /// <summary> /// 地区控件数据访问操作层 /// </summary> public static class AreaDAL ... { private static readonly string Link = " server=gou200;user=gou;pwd=gou; " ;
private static readonly string [] SecondDomain = new string [] ... { " www " , " gou " , " un " , " help " , " hotel " , " news " , " trip " , " brand " , " soft " , " train " } ;
private static readonly int CookieDay = 3 ; // Cookie 保存的天数 private static readonly string CookieDomain = " xxx.cn " ; // Cookie 的域 /**/ /// <summary> /// 返回符合 SQL语句 的 DataTable /// </summary> /// <param name="sql"> SQL 语句 </param> /// <returns></returns> public static DataTable GetDataTable( string sql) ... { OracleConnection conn = new OracleConnection(Link); OracleDataAdapter adapter = new OracleDataAdapter(sql, conn); DataTable table = new DataTable(); adapter.Fill(table); return table; } /**/ /// <summary> /// 将当前所有下拉框的值存入 Cookie /// </summary> /// <param name="sfid"> 省份ID </param> /// <param name="csid"> 城市ID </param> /// <param name="xianid"> 县/区ID </param> /// <param name="zhenid"> 镇ID </param> /// <param name="sf"> 省份名 </param> /// <param name="cs"> 城市名 </param> /// <param name="xian"> 县/区名 </param> /// <param name="zhen"> 镇名 </param> /// <param name="domain"> 地区域名 </param> public static void SaveCookie( int sfid, int csid, int xianid, int zhenid, string sf, string cs, string xian, string zhen, string domain) ... { HttpCookie cookie = new HttpCookie( " Area " ); cookie[ " SFID " ] = sfid.ToString(); cookie[ " CSID " ] = csid.ToString(); cookie[ " XianID " ] = xianid.ToString(); cookie[ " ZhenID " ] = zhenid.ToString(); cookie[ " SF " ] = HttpContext.Current.Server.UrlEncode(String.IsNullOrEmpty(sf) ? "" : sf.Replace( " —请选择— " , "" )); cookie[ " CS " ] = HttpContext.Current.Server.UrlEncode(String.IsNullOrEmpty(cs) ? "" : cs.Replace( " —请选择— " , "" )); cookie[ " Xian " ] = HttpContext.Current.Server.UrlEncode(String.IsNullOrEmpty(xian) ? "" : xian.Replace( " —请选择— " , "" )); cookie[ " Zhen " ] = HttpContext.Current.Server.UrlEncode(String.IsNullOrEmpty(zhen) ? "" : zhen.Replace( " —请选择— " , "" )); cookie[ " Domain " ] = domain; cookie.Domain = CookieDomain; cookie.Expires = DateTime.Now.AddDays(CookieDay); HttpContext.Current.Response.Cookies.Add(cookie); } /**/ /// <summary> /// 将地区信息存入 Cookie /// </summary> /// <param name="ai"> 地区信息 </param> private static void SaveCookie(AreaInfo ai) ... { SaveCookie(ai.SFID, ai.CSID, ai.XianID, ai.ZhenID, ai.SF, ai.CS, ai.Xian, ai.Zhen, ai.Domain); } /**/ /* ===================================================================== */ /**/ /// <summary> /// 通过 域名 加载相应的地区 /// </summary> /// <returns></returns> public static AreaInfo GetAreaInfoByDomain() ... { string domain = HttpContext.Current.Request.Url.Host.Split( ' . ' )[ 0 ].ToLower(); if (IsSecondDomain(domain)) return null ; AreaInfo ai; HttpCookie cookie = HttpContext.Current.Request.Cookies[ " Area " ]; if (cookie != null && cookie[ " Domain " ] == domain) ... { // Cookie 里的域名和当前的域名相同,说明 Cookie 里有更详细的地区信息(如:从 Cookie 重定向而来) ai = new AreaInfo(); ai.SFID = Convert.ToInt32(cookie[ " SFID " ]); ai.CSID = Convert.ToInt32(cookie[ " CSID " ]); ai.XianID = Convert.ToInt32(cookie[ " XianID " ]); ai.ZhenID = Convert.ToInt32(cookie[ " ZhenID " ]); ai.SF = HttpContext.Current.Server.UrlDecode(cookie[ " SF " ]); ai.CS = HttpContext.Current.Server.UrlDecode(cookie[ " CS " ]); ai.Xian = HttpContext.Current.Server.UrlDecode(cookie[ " Xian " ]); ai.Zhen = HttpContext.Current.Server.UrlDecode(cookie[ " Zhen " ]); ai.Domain = cookie[ " Domain " ]; } else ... { ai = GetAreaInfoByDomain(domain); if (ai == null ) return null ; SaveCookie(ai); } return ai; } /**/ /// <summary> /// 返回域名对应的地区信息 /// </summary> /// <param name="domain"> 三级域名 </param> /// <returns></returns> private static AreaInfo GetAreaInfoByDomain( string domain) ... { OracleConnection conn = new OracleConnection(Link); // 使用一条SQL 代替 多次SQL查询 string sql = String.Format( " SELECT cs.csid,(cs.sfid || sf.sfid) AS sfid,cs.CityName,(cs.sfname || sf.sfname) AS sfname FROM (SELECT c.pkid AS csid,c.OneID AS sfid,c.CityName AS CityName,s.sfname AS sfname FROM (SELECT PKID,OneID,CityName FROM CityInfo WHERE CityDomain='{0}') c RIGHT JOIN(SELECT pkid,CityName AS sfname FROM CityInfo WHERE pkid=(SELECT OneID FROM CityInfo WHERE CityDomain='{0}')) s ON c.oneid=s.pkid) cs FULL OUTER JOIN (SELECT pkid AS sfid,CityName AS sfname FROM cityinfo WHERE citydomain='{0}' AND OneID=0) sf ON cs.sfid=sf.sfid " , domain); OracleCommand cmd = new OracleCommand(sql, conn); conn.Open(); OracleDataReader dr = cmd.ExecuteReader(); if (dr.Read()) ... { AreaInfo ai = new AreaInfo(); Object[] os = new object [dr.FieldCount]; dr.GetValues(os); dr.Close(); conn.Close(); ai.CSID = (os[ 0 ] is DBNull) ? 0 : Convert.ToInt32(os[ 0 ]); ai.SFID = (os[ 1 ] is DBNull) ? 0 : Convert.ToInt32(os[ 1 ]); ai.CS = (os[ 2 ] is DBNull) ? String.Empty : os[ 2 ].ToString(); ai.SF = (os[ 3 ] is DBNull) ? String.Empty : os[ 3 ].ToString(); ai.Domain = domain; return ai; } dr.Close(); conn.Close(); return null ; } /**/ /// <summary> /// 是否是二级域名 /// </summary> /// <param name="domain"> 可能是二级域名的字符串 </param> /// <returns> 是,返回 ture </returns> private static bool IsSecondDomain( string domain) ... { foreach ( string sd in SecondDomain) ... { if (domain == sd) return true ; } return false ; } /**/ /* ===================================================================== */ /**/ /// <summary> /// 返回 Cookie 对应的地区信息 /// </summary> /// <returns></returns> public static AreaInfo GetAreaInfoByCookie() ... { AreaInfo ai = new AreaInfo(); HttpCookie cookie = HttpContext.Current.Request.Cookies[ " Area " ]; if (cookie == null ) return null ; ai.SFID = Convert.ToInt32(cookie[ " SFID " ]); ai.CSID = Convert.ToInt32(cookie[ " CSID " ]); ai.XianID = Convert.ToInt32(cookie[ " XianID " ]); ai.ZhenID = Convert.ToInt32(cookie[ " ZhenID " ]); ai.SF = HttpContext.Current.Server.UrlDecode(cookie[ " SF " ]); ai.CS = HttpContext.Current.Server.UrlDecode(cookie[ " CS " ]); ai.Xian = HttpContext.Current.Server.UrlDecode(cookie[ " Xian " ]); ai.Zhen = HttpContext.Current.Server.UrlDecode(cookie[ " Zhen " ]); ai.Domain = cookie[ " Domain " ]; if (ai.SFID == 0 ) return null ; return ai; } /**/ /* ===================================================================== */ /**/ /// <summary> /// 通过 IP 加载相应的地区 /// </summary> /// <returns></returns> public static AreaInfo GetAreaInfoByIP() ... { AreaInfo ai = GetAreaInfoByIP(HttpContext.Current.Request.ServerVariables[ " REMOTE_ADDR " ]); if (ai == null ) return null ; SaveCookie(ai); return ai; } /**/ /// <summary> /// 返回 IP 对应的地区信息 /// </summary> /// <param name="ip"> IP字符串 </param> /// <returns></returns> private static AreaInfo GetAreaInfoByIP( string ip) ... { AreaInfo ai = new AreaInfo(); uint IpNum; string [] IPList = ip.Split( ' . ' ); IpNum = Convert.ToUInt32(Convert.ToUInt32(IPList[ 0 ]) * 255 * 255 * 255 + Convert.ToUInt32(IPList[ 1 ]) * 255 * 255 + Convert.ToUInt32(IPList[ 2 ]) * 255 + Convert.ToUInt32(IPList[ 3 ])); string sql = String.Format( " SELECT i.sfid00,i.CityCode,i.sfname,i.CityName,c.CityDomain FROM (SELECT CityDomain,pkid FROM CityInfo) c RIGHT JOIN (SELECT SFID00,CityCode,SFName,CityName FROM T_BM_Ip WHERE IpOne<={0} AND IpTwo>={0}) i ON c.pkid=i.CityCode " , IpNum); OracleConnection conn = new OracleConnection(Link); OracleCommand cmd = new OracleCommand(sql, conn); conn.Open(); OracleDataReader dr = cmd.ExecuteReader(); if (dr.Read()) ... { Object[] os = new object [dr.FieldCount]; dr.GetValues(os); dr.Close(); conn.Close(); ai.SFID = (os[ 0 ] is DBNull) ? 0 : Convert.ToInt32(os[ 0 ]); ai.CSID = (os[ 1 ] is DBNull) ? 0 : Convert.ToInt32(os[ 1 ]); ai.SF = (os[ 2 ] is DBNull) ? String.Empty : os[ 2 ].ToString(); ai.CS = (os[ 3 ] is DBNull) ? String.Empty : os[ 3 ].ToString(); ai.Domain = (os[ 4 ] is DBNull) ? String.Empty : os[ 4 ].ToString(); return ai; } dr.Close(); conn.Close(); ai.SFID = 11 ; // 设置默认值 ai.SF = " 北京 " ; ai.Domain = " bj " ; return ai; } } }
然后就是重点了...用户控件 前台代码,中间有段很长的 省份城市JS 数组,我去掉了,想看可以去另一篇同名 blog 看
<%
@ Control Language
=
"
C#
"
AutoEventWireup
=
"
true
"
CodeFile
=
"
AreaControl.ascx.cs
"
Inherits
=
"
AreaControl
"
%>
<
div
>
<
table id
=
"
AreaTable
"
runat
=
"
server
"
>
<
tr
>
<
td
>
<
select id
=
"
sSF
"
runat
=
"
server
"
>
<
option selected
=
"
selected
"
value
=
"
0
"
>
—请选择—
</
option
>
</
select
>
</
td
>
<
td
>
<
select id
=
"
sCS
"
runat
=
"
server
"
>
<
option selected
=
"
selected
"
value
=
"
0
"
>
—请选择—
</
option
>
</
select
>
</
td
>
<
td
>
<
select id
=
"
sXian
"
runat
=
"
server
"
>
<
option selected
=
"
selected
"
value
=
"
0
"
>
—请选择—
</
option
>
</
select
>
</
td
>
<
td
>
<
select id
=
"
sZhen
"
runat
=
"
server
"
>
<
option selected
=
"
selected
"
value
=
"
0
"
>
—请选择—
</
option
>
</
select
>
</
td
>
</
tr
>
</
table
>
<
input type
=
"
hidden
"
id
=
"
hSF
"
runat
=
"
server
"
/>
<
input type
=
"
hidden
"
id
=
"
hCS
"
runat
=
"
server
"
/>
<
input type
=
"
hidden
"
id
=
"
hXian
"
runat
=
"
server
"
/>
<
input type
=
"
hidden
"
id
=
"
hZhen
"
runat
=
"
server
"
/>
<
input type
=
"
hidden
"
id
=
"
hDomain
"
runat
=
"
server
"
/>
</
div
>
<
script type
=
"
text/javascript
"
>
//
<%-- ID 城市 上级ID 二级域名--%>
var
aDQ
=
new
Array();
//
<%-- 省份的上级ID改为 -1 和 "—请选择—" 区分开 --%>
aDQ[
0
]
=
[
"
11
"
,
"
北京
"
,
"
-1
"
,
"
bj
"
];
//
该数组请去另一篇 Blog 看
//
<%-- 通过 DOM 创建下拉框 --%>
//
<%-- obj:下拉框的 options 数据集 --%>
//
<%-- id:下拉框的 id --%>
function
CreateDropDownList(obj,id)
...
{ var sel = document.getElementById(id); if (sel == null ) // <%-- 可能只显示省市 --%> return ; sel.options.length = 1 ; // <%-- 保存"—请选择—" --%> // <%-- 对于省份、城市常用 Array 存储,对于县、镇则从数据库读取 --%> if (obj == null ) return ; if (obj instanceof Array) ... { for ( var i = 0 ;i < obj.length;i ++ ) ... { var op = document.createElement( " option " ); op.setAttribute( " value " ,obj[i][ 0 ]); // <%-- 可考虑改成 value="xm,厦门">厦门 --%> var txt = document.createTextNode(obj[i][ 1 ]); op.appendChild(txt); sel.appendChild(op); } } else ... { for ( var i = 0 ;i < obj.Rows.length;i ++ ) // <%-- 注意这里是 length 不是 Count --%> ... { var op = document.createElement( " option " ); op.setAttribute( " value " ,obj.Rows[i].ID); // <%-- 注意区分大小写 --%> var txt = document.createTextNode(obj.Rows[i].NAME); // <%-- 根 DataTable 的列名称要一致 --%> op.appendChild(txt); sel.appendChild(op); } } // <%-- 绑定下一个下拉框 --%> switch (id) ... { // <%-- 选择一次,保存到 Cookie 一次,可以往 onchange 事件里加入 Save(); --%> case " <%=this.sSF.ClientID%> " :sel.onchange = function () ... {ClearXianZhen();CreateDropDownList(GetArray( this .value), " <%=this.sCS.ClientID%> " );Save();} ; break ; case " <%=this.sCS.ClientID%> " :sel.onchange = function () ... {ClearZhen();CreateDropDownList(AreaControl.GetDataTable( this .value,AreaEnum.Xian).value, " <%=this.sXian.ClientID%> " );Save();} ; break ; // <%-- JS 向 C# 传递参数,并取得 C# 的返回值 --%> case " <%=this.sXian.ClientID%> " :sel.onchange = function () ... {CreateDropDownList(AreaControl.GetDataTable( this .value,AreaEnum.Zhen).value, " <%=this.sZhen.ClientID%> " );Save();} ; break ; // <%-- 返回值.value --%> default :sel.onchange = function () ... {Save();} ; break ; // alert(this.value); } }
//
<%-- 获取要求的数组 --%>
//
<%-- val:所属的省份ID --%>
function
GetArray(val)
...
{ if (val == 0 ) // <%-- 用户选"—请选择—"不查询 --%> return null ; var temp = new Array(); var j = 0 ; for ( var i = 0 ;i < aDQ.length;i ++ ) ... { if (val == aDQ[i][ 2 ].toString()) ... { temp[j] = aDQ[i]; j ++ ; } } return temp; }
//
<%-- 清空"镇"下拉框 --%>
function
ClearZhen()
...
{ CreateDropDownList( null , " <%=this.sZhen.ClientID%> " ); }
//
<%-- 清空"县"和"镇"下拉框 --%>
function
ClearXianZhen()
...
{ CreateDropDownList( null , " <%=this.sXian.ClientID%> " ); CreateDropDownList( null , " <%=this.sZhen.ClientID%> " ); }
//
<%-- 将当前所有下拉框的值存入 Cookie --%>
function
Save()
...
{ var sf = Single( " <%=this.sSF.ClientID%> " , " <%=this.hSF.ClientID%> " ); var cs = Single( " <%=this.sCS.ClientID%> " , " <%=this.hCS.ClientID%> " ); var xian = Single( " <%=this.sXian.ClientID%> " , " <%=this.hXian.ClientID%> " ); var zhen = Single( " <%=this.sZhen.ClientID%> " , " <%=this.hZhen.ClientID%> " ); var domain = GetDomain((cs.ID == 0 ) ? sf.ID:cs.ID); document.getElementById( " <%=this.hDomain.ClientID%> " ).value = domain; // <%-- 暂时不存 Cookie ,只存到隐藏控件里 --%> // <%-- AreaControl.SaveCookie(sf.ID,cs.ID,xian.ID,zhen.ID,sf.Name,cs.Name,xian.Name,zhen.Name,domain); --%> }
//
<%-- 返回单个下拉框的值和文本 --%>
//
<%-- sid:下拉框ID --%>
//
<%-- hid:隐藏控件ID --%>
function
Single(sid,hid)
...
{ var sel = document.getElementById(sid); var a = new Area(); if (sel == null ) // <%-- 可能只显示省市 --%> return a; a.ID = sel.value; // <%-- 取得下拉框的 value --%> a.Name = sel.options[sel.selectedIndex].text; // <%-- 取得下拉框的 text --%> document.getElementById(hid).value = a.Name; // <%-- text 另存到隐藏控件里 --%> return a; }
//
<%-- 构造函数 --%>
function
Area()
...
{ this .ID; // <%-- 下拉框的值 --%> this .Name; // <%-- 下拉框的文本 --%> }
//
<%-- 返回 ID 对应的域名 --%>
//
<%-- id:地区ID --%>
function
GetDomain(id)
...
{ for ( var i = 0 ;i < aDQ.length;i ++ ) ... { if (aDQ[i][ 0 ] == id) return aDQ[i][ 3 ]; } return "" ; }
/**/
/* <%------------------------------------------------------------------%> */
//
<%-- 通过 域名 加载相应的地区 --%>
function
LoadAreaByDomain()
...
{ var ai = AreaControl.GetAreaInfoByDomain().value; if (ai == null ) return false ; Bind(ai); return true ; }
//
<%-- 根据地区信息绑定相应的下拉框 --%>
//
<%-- ai:地区信息类 --%>
function
Bind(ai)
...
{ bind(ai.SFID,ai.CSID,ai.XianID,ai.ZhenID); }
//
<%-- 根据地区信息绑定相应的下拉框 --%>
//
<%-- 因为JS不支持重载写成2个方法 --%>
function
bind(sfid,csid,xianid,zhenid)
...
{ var index; if (sfid != 0 ) ... { index = Val2Index(sfid, " <%=this.sSF.ClientID%> " ); document.getElementById( " <%=this.sSF.ClientID%> " ).selectedIndex = index; document.getElementById( " <%=this.hSF.ClientID%> " ).value = Index2Text(index, " <%=this.sSF.ClientID%> " ); // <%-- text 另存到隐藏控件里 --%> CreateDropDownList(GetArray(sfid.toString()), " <%=this.sCS.ClientID%> " ); } if (csid != 0 ) ... { index = Val2Index(csid, " <%=this.sCS.ClientID%> " ); document.getElementById( " <%=this.sCS.ClientID%> " ).selectedIndex = index; document.getElementById( " <%=this.hCS.ClientID%> " ).value = Index2Text(index, " <%=this.sCS.ClientID%> " ); CreateDropDownList(AreaControl.GetDataTable(csid.toString(),AreaEnum.Xian).value, " <%=this.sXian.ClientID%> " ) // <%-- 要把 int 型的转换成字符串--%> } if (xianid != 0 && document.getElementById( " <%=this.sXian.ClientID%> " ) != null ) // <%-- 可能只显示省市 --%> ... { index = Val2Index(xianid, " <%=this.sXian.ClientID%> " ); document.getElementById( " <%=this.sXian.ClientID%> " ).selectedIndex = index; document.getElementById( " <%=this.hXian.ClientID%> " ).value = Index2Text(index, " <%=this.sXian.ClientID%> " ); CreateDropDownList(AreaControl.GetDataTable(xianid.toString(),AreaEnum.Zhen).value, " <%=this.sZhen.ClientID%> " ); } if (zhenid != 0 && document.getElementById( " <%=this.sZhen.ClientID%> " ) != null ) ... { index = Val2Index(zhenid, " <%=this.sZhen.ClientID%> " ); document.getElementById( " <%=this.sZhen.ClientID%> " ).selectedIndex = index; document.getElementById( " <%=this.hZhen.ClientID%> " ).value = Index2Text(index, " <%=this.sZhen.ClientID%> " ); } document.getElementById(" <%=this.hDomain.ClientID%> " ).value = GetDomain((csid == 0 ) ? sfid:csid); }
//
<%-- 将 <select> 的 value 换算成 selectedIndex --%>
//
<%-- val:下拉框的值 --%>
//
<%-- id:下拉框 ID --%>
function
Val2Index(val,id)
...
{ var sel = document.getElementById(id); for ( var i = 0 ;i < sel.options.length;i ++ ) ... { if (val == sel.options[i].value) return i; } return 0 ; }
//
<%-- 将 <select> 的 value 换算成 text --%>
//
<%-- index:下拉框的值 --%>
//
<%-- id:下拉框 ID --%>
function
Index2Text(index,id)
...
{ return document.getElementById(id).options[index].text; }
/**/
/* <%------------------------------------------------------------------%> */
//
<%-- 通过 cookie 加载相应的地区 --%>
function
LoadAreaByCookie()
...
{ var ai = AreaControl.GetAreaInfoByCookie().value; if (ai == null ) return false ; // <%-- 根据地区域名重定向 --%> if ( <%= this .IsRedirect.ToString().ToLower() %> && ai.Domain != null && ai.Domain != "" && ai.Domain != window.location.hostname.split( " . " )[ 0 ]) ... { window.location.href = window.location.href.substring( 0 , 7 ) + ai.Domain + " . " + window.location.href.slice( 7 ); return true ; // <%-- 返回真,不执行后面的判断,直接页面重定向 --%> } Bind(ai); return true ; }
/**/
/* <%------------------------------------------------------------------%> */
//
<%-- 通过 IP 加载相应的地区 --%>
function
LoadAreaByIP()
...
{ var ai = AreaControl.GetAreaInfoByIP().value; if (ai == null ) return false ; if ( <%= this .IsRedirect.ToString().ToLower() %> && ai.Domain != null && ai.Domain != "" && ai.Domain != window.location.hostname.split( " . " )[ 0 ]) ... { window.location.href = window.location.href.substring( 0 , 7 ) + ai.Domain + " . " + window.location.href.slice( 7 ); return true ; } Bind(ai); return true ; }
/**/
/* <%------------------------------------------------------------------%> */
//
<%-- 执行入口 --%>
var
arr
=
GetArray(
"
-1
"
); CreateDropDownList(arr,
"
<%=this.sSF.ClientID%>
"
);
//
<%-- 是否只显示省份和城市 --%>
if
(
<%=
this
.OnlySFCS.ToString().ToLower()
%>
)
//
<%-- JS 的布尔值是小写 --%>
...
{ var table = document.getElementById( " <%=this.AreaTable.ClientID%> " ); table.rows[ 0 ].deleteCell( 3 ); // <%-- 删除后行索引变小,因此要从后面开始删除 --%> table.rows[ 0 ].deleteCell( 2 ); }
if
(
<%=
this
.SFID
%>==
0
)
//
<%-- 没赋值才根据 域名->Cookie->IP 绑定下拉框 --%>
(LoadAreaByDomain())
?
""
: ((LoadAreaByCookie())
?
""
: LoadAreaByIP());
else
//
<%-- 只要省份有值就绑定 --%>
bind(
<%=
this
.SFID
%>
,
<%=
this
.CSID
%>
,
<%=
this
.XianID
%>
,
<%=
this
.ZhenID
%>
);
//
<%-- 赋值绑定 --%>
//
<%-- 是否垂直显示 --%>
if
(
<%=
this
.IsVertical.ToString().ToLower()
%>
)
...
{ var h = document.getElementById( " <%=this.AreaTable.ClientID%> " ); var v = document.createElement( " table " ); v.setAttribute( " id " , " <%=this.AreaTable.ClientID%> " ); InsertRow(h.rows[ 0 ].cells.length); h.parentNode.replaceChild(v,h); // <%-- 注意这里要通过添加 <div> 来解决 ascx 不能操作 aspx 的问题 --%> }
//
<%-- 循环创建(将列变成行) --%>
//
<%-- length:循环次数 --%>
function
InsertRow(length)
...
{ for ( var i = 0 ;i < length;i ++ ) ... { v.insertRow(i); v.rows[i].insertCell( 0 ); v.rows[i].cells[ 0 ].appendChild(h.rows[ 0 ].cells[i].childNodes[ 0 ]); // <%-- 将 cell 的的元素剪贴走 --%> } }
//
<%-- 是否显示在下拉框前显示名称(省份、城市、县、镇) --%>
if
(
<%=
this
.IsDisplayName.ToString().ToLower()
%>
)
...
{ var sel = document.getElementById( " <%=this.sSF.ClientID%> " ); sel.parentNode.insertBefore(document.createTextNode( " 省份: " ),sel); sel = document.getElementById( " <%=this.sCS.ClientID%> " ); sel.parentNode.insertBefore(document.createTextNode( " 城市: " ),sel); sel = document.getElementById( " <%=this.sXian.ClientID%> " ); if (sel != null ) sel.parentNode.insertBefore(document.createTextNode( " 县区: " ),sel); sel = document.getElementById( " <%=this.sZhen.ClientID%> " ); if (sel != null ) sel.parentNode.insertBefore(document.createTextNode( " 乡镇: " ),sel); }
//
<%-- 是否禁用控件 --%>
if
(
<%=
this
.IsDisabled.ToString().ToLower()
%>
)
...
{ var sels = document.getElementById( " <%=this.AreaTable.ClientID%> " ).getElementsByTagName( " select " ); for ( var i = 0 ;i < sels.length;i ++ ) sels[i].setAttribute( " disabled " , " disabled " ); }
</
script
>
最后就是用户控件的后台页了.
using
System;
using
System.Data;
using
System.Data.OracleClient;
using
System.Web.UI.MobileControls;
using
AreaDropDownList;
public
partial
class
AreaControl : System.Web.UI.UserControl
...
{ 属性 #region 属性 private int _SFID; private int _CSID; private int _XianID; private int _ZhenID; private string _SF; private string _CS; private string _Xian; private string _Zhen; private string _Domain; private bool _OnlySFCS; private bool _IsRedirect; private bool _IsVertical; private bool _IsDisplayName; private bool _IsDisabled; // 地区ID public int SFID ... { get ... { return this ._SFID; } set ... { this ._SFID = value; } } public int CSID ... { get ... { return this ._CSID; } set ... { this ._CSID = value; } } public int XianID ... { get ... { return this ._XianID; } set ... { this ._XianID = value; } } public int ZhenID ... { get ... { return this ._ZhenID; } set ... { this ._ZhenID = value; } } // 地区名 public string SF ... { get ... { return this ._SF; } set ... { this ._SF = value; } } public string CS ... { get ... { return this ._CS; } set ... { this ._CS = value; } } public string Xian ... { get ... { return this ._Xian; } set ... { this ._Xian = value; } } public string Zhen ... { get ... { return this ._Zhen; } set ... { this ._Zhen = value; } } // 地区域名 public string Domain ... { get ... { return this ._Domain; } set ... { this ._Domain = value; } } /**/ /* ************* 特殊属性 *************** */ // 是否重定向(通过 Cookie 和 IP 绑定下拉框会重定向) public bool IsRedirect ... { get ... { return this ._IsRedirect; } set ... { this ._IsRedirect = value; } } // 只显示省份城市 public bool OnlySFCS ... { get ... { return this ._OnlySFCS; } set ... { this ._OnlySFCS = value; } } // 是否垂直显示 public bool IsVertical ... { get ... { return this ._IsVertical; } set ... { this ._IsVertical = value; } } // 是否显示在下拉框前显示名称(省份、城市、县、镇) public bool IsDisplayName ... { get ... { return this ._IsDisplayName; } set ... { this ._IsDisplayName = value; } } // 是否禁用控件 public bool IsDisabled ... { get ... { return this ._IsDisabled; } set ... { this ._IsDisabled = value; } } #endregion // 在 aspx 取值用(赋值用 JS) protected void Page_Init() ... { // 为了在 aspx 页面的 按钮事件里取值 if ( this .IsPostBack) ... { this .SFID = Convert.ToInt32( this .Request.Form[ this .sSF.UniqueID]); this .CSID = Convert.ToInt32( this .Request.Form[ this .sCS.UniqueID]); this .XianID = Convert.ToInt32( this .Request.Form[ this .sXian.UniqueID]); this .ZhenID = Convert.ToInt32( this .Request.Form[ this .sZhen.UniqueID]); this .SF = this .Request.Form[ this .hSF.UniqueID].Replace( " —请选择— " , "" ); this .CS = this .Request.Form[ this .hCS.UniqueID].Replace( " —请选择— " , "" ); this .Xian = this .Request.Form[ this .hXian.UniqueID].Replace( " —请选择— " , "" ); this .Zhen = this .Request.Form[ this .hZhen.UniqueID].Replace( " —请选择— " , "" ); this .Domain = this .Request.Form[ this .hDomain.UniqueID]; } } protected void Page_Load( object sender, EventArgs e) ... { // 将C#的类和枚举分别注册到JS里 AjaxPro.Utility.RegisterTypeForAjax( typeof (AreaControl)); AjaxPro.Utility.RegisterEnumForAjax( typeof (AreaEnum)); } /**/ /// <summary> /// 将当前下拉框对应的地区信息存入 Cookie(供"保存"按钮使用) /// </summary> public void SaveCookie() ... { AreaDAL.SaveCookie( this .SFID, this .CSID, this .XianID, this .ZhenID, this .SF, this .CS, this .Xian, this .Zhen, this .Domain); } /**/ /* ====================== 以下是为防止命名空间暴露在 JS 里 ========================== */ /**/ /// <summary> /// 取得下拉框所需数据 /// </summary> /// <param name="UpID"> 上一级ID </param> /// <param name="ae"> 县/镇 </param> /// <returns> 返回 DataTable </returns> [AjaxPro.AjaxMethod] public static DataTable GetDataTable( string UpID, AreaEnum ae) ... { if (UpID == " 0 " || String.IsNullOrEmpty(UpID)) // 用户选"—请选择—"不查询 return null ; // 处于安全性考虑采用 列别名 // Oracle 返回的 DataTable 里的列名全是大写,在页面取值时记得大写 string sql; if (Enum.Equals(ae, AreaEnum.Xian)) sql = " SELECT PKID AS id,CityName AS name,OneID,CityDomain FROM CityInfo WHERE THREEID=0 AND TWOID= " + UpID; else sql = " SELECT PKID AS id,CityName AS name,OneID,CityDomain FROM CityInfo WHERE THREEID= " + UpID; return AreaDAL.GetDataTable(sql); } /**/ /// <summary> /// 将当前所有下拉框的值存入 Cookie /// </summary> /// <param name="sfid"> 省份ID </param> /// <param name="csid"> 城市ID </param> /// <param name="xianid"> 县/区ID </param> /// <param name="zhenid"> 镇ID </param> /// <param name="sf"> 省份名 </param> /// <param name="cs"> 城市名 </param> /// <param name="xian"> 县/区名 </param> /// <param name="zhen"> 镇名 </param> /// <param name="domain"> 地区域名 </param> [AjaxPro.AjaxMethod] public static void SaveCookie( int sfid, int csid, int xianid, int zhenid, string sf, string cs, string xian, string zhen, string domain) ... { AreaDAL.SaveCookie(sfid, csid, xianid, zhenid, sf, cs, xian, zhen, domain); } /**/ /* ===================================================================== */ /**/ /// <summary> /// 通过 域名 加载相应的地区 /// </summary> /// <returns></returns> [AjaxPro.AjaxMethod] public static AreaInfo GetAreaInfoByDomain() ... { return AreaDAL.GetAreaInfoByDomain(); } /**/ /* ===================================================================== */ /**/ /// <summary> /// 返回 Cookie 对应的地区信息 /// </summary> /// <returns></returns> [AjaxPro.AjaxMethod] public static AreaInfo GetAreaInfoByCookie() ... { return AreaDAL.GetAreaInfoByCookie(); } /**/ /* ===================================================================== */ /**/ /// <summary> /// 通过 IP 加载相应的地区 /// </summary> /// <returns></returns> [AjaxPro.AjaxMethod] public static AreaInfo GetAreaInfoByIP() ... { return AreaDAL.GetAreaInfoByIP(); } }
/**/
/// <summary> /// 地区枚举(如果数据库设计合理,前两位省份,中间两位城市,最后两位县/区,那么就不需要这个枚举,代码量也少很多) /// </summary>
public
enum
AreaEnum
...
{ /**/ /// <summary> /// 省 /// </summary> SF, /**/ /// <summary> /// 市 /// </summary> CS, /**/ /// <summary> /// 县 /// </summary> Xian, /**/ /// <summary> /// 镇 /// </summary> Zhen }
完成
最后是测试页
using
System;
using
System.Data;
using
System.Configuration;
using
System.Collections;
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_Init() ... { // this.AreaControl1.OnlySFCS = true; } protected void Page_Load( object sender, EventArgs e) ... { // 要通过给控件赋值,绑定相应地区,只需给 ID 即可,Name就不用了 // this.AreaControl1.SFID = 42; // this.AreaControl1.CSID = 4205; // this.AreaControl1.XianID = 420521; // this.AreaControl1.ZhenID=35020303; // this.AreaControl1.OnlySFCS = true; this .AreaControl1.IsVertical = true ; this .AreaControl1.IsDisplayName = true ; // this.AreaControl1.IsDisabled=true; } protected void Button1_Click( object sender, EventArgs e) ... { // 添加一个按钮 用来读取 用户选择的地区信息 Response.Write(String.Format( " {0}{4},{1}{5},{2}{6},{3}{7} " , AreaControl1.SF, AreaControl1.CS, AreaControl1.Xian, AreaControl1.Zhen, AreaControl1.SFID, AreaControl1.CSID, AreaControl1.XianID, AreaControl1.ZhenID)); } protected void Button2_Click( object sender, EventArgs e) ... { // 直接调用 AreaControl 里的 SaveCookie 把 用户选择的地区存入 Cookie (这是为了方便,集成的) this .AreaControl1.SaveCookie(); } }
<%
...
@ Page Language = " C# " AutoEventWireup = " true " CodeFile = " Default.aspx.cs " Inherits = " _Default "
%>
<%
...
@ Register Src = " AreaControl.ascx " TagName = " AreaControl " TagPrefix = " uc1 "
%>
<!
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
>
无标题页
</
title
>
</
head
>
<
body
>
<
form
id
="form1"
runat
="server"
>
<
div
>
<
uc1:AreaControl
id
="AreaControl1"
runat
="server"
></
uc1:AreaControl
>
<
asp:Button
ID
="Button1"
runat
="server"
Text
="跳转"
OnClick
="Button1_Click"
/>
<
asp:Button
ID
="Button2"
runat
="server"
Text
="存 Cookie"
OnClick
="Button2_Click"
/>
</
div
>
</
form
>
</
body
>
</
html
>
记得 是 AjaxPro 6.10.4.1 的版本
Web.Config 在<system.web>来上这句
<
httpHandlers
>
<
add
verb
="POST,GET"
path
="ajaxpro/*.ashx"
type
="AjaxPro.AjaxHandlerFactory, AjaxPro.2"
/>
</
httpHandlers
>