效果:

避免了选择item时页面刷新或ajax延迟。
测试用的数据库脚本和数据:
create
table
tbl1
(
CityID
int
primary
key
identity
(
1
,
1
),
CityName
nvarchar
(
50
)
not
null
,
)
go
create
table
tbl2
(
AreaID
int
primary
key
identity
(
1
,
1
),
AreaName
nvarchar
(
50
)
not
null
,
AreaCity
int
references
tbl1(CityID)
)
go
create
table
tbl3
(
BuildingID
int
primary
key
identity
(
1
,
1
),
BuildingName
nvarchar
(
50
)
not
null
,
BuildingArea
int
references
tbl2(AreaID)
)
go

insert
into
tbl1
values
(
'
淄博
'
)
insert
into
tbl1
values
(
'
青岛
'
)

insert
into
tbl2
values
(
'
张店
'
,
1
)
insert
into
tbl2
values
(
'
辛店
'
,
1
)
insert
into
tbl2
values
(
'
四方
'
,
2
)
insert
into
tbl2
values
(
'
市南
'
,
2
)

insert
into
tbl3
values
(
'
Building1
'
,
1
)
insert
into
tbl3
values
(
'
Building2
'
,
1
)
insert
into
tbl3
values
(
'
Building3
'
,
1
)
insert
into
tbl3
values
(
'
Building4
'
,
2
)
insert
into
tbl3
values
(
'
Building5
'
,
3
)
insert
into
tbl3
values
(
'
Building6
'
,
3
)
insert
into
tbl3
values
(
'
Building7
'
,
4
)
insert
into
tbl3
values
(
'
Building8
'
,
4
)
注意表间关系。
使用:
导入前缀
<%@ Register TagPrefix="cc1" Namespace="sss" Assembly="构架测试" %>
定义控件
<cc1:CascadeDropdownLists ID="myDropdown" Direction="Vertical" runat="server" DropDownListStyle-BackColor="AliceBlue" DropDownListStyle-Width="160px"/>
数据绑定
DataTable[] dts = new DataTable[] { new DataTable(), new DataTable() };
SqlDataAdapter da = new SqlDataAdapter("select * from tbl1", constr);
da.Fill(dts[0]);
da.SelectCommand.CommandText = "select * from tbl2";
da.Fill(dts[1]);
myDropdown.DataSource = dts;
myDropdown.DataBind();
原理就是每个datatable对应一个下拉列表,其中的记录对应下拉列表的选项。
通过客户端脚本根据各下拉列表间的关系控制那些选项显示,哪些不显示。
难点在于控件状态的保持。
实现了简单的样式控制。
优点在于速度快,显示效果好。
缺点是必须要用viewstate,否则无法取回提交数据。需要改进。
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;
using
System.ComponentModel;
using
System.Collections.Generic;
using
System.Collections.Specialized;

namespace
sss

...
{
[ToolboxData("<{0}:CascadeDropdownLists runat="server"></{0}:CascadeDropdownLists>")]
public class CascadeDropdownLists : WebControl//, INamingContainer,IPostBackEventHandler, IPostBackDataHandler

...{

Properties#region Properties


/**//// <summary>
/// 需要有2至三个字段,第一个字段是在下拉列表内唯一选项标识;第二个字段是选项文本,第三个字段是上级选项的标识
/// </summary>
[Browsable(true)]
[Category("Data")]
[Description("用于分别绑定各级下拉列表的DataTable数组")]
public DataTable[] DataSource

...{
get

...{
return _dataSource;
}

set

...{
_dataSource = value;
}
}

[Browsable(true)]
[Category("Appearance")]
[Description("指定排列多个下拉列表的方向")]
public RepeatDirection Direction

...{
get

...{
return ViewState["Direction"] == null ? RepeatDirection.Horizontal : (RepeatDirection)ViewState["Direction"];
}

set

...{
ViewState["Direction"] = value;
}
}

protected override HtmlTextWriterTag TagKey

...{
get

...{
return HtmlTextWriterTag.Table;
}
}

[Browsable(false)]
public string[] PostBackValues

...{
get

...{
string[] rval = new string[_dropDownLists.Count];
for (int i = 0; i < _dropDownLists.Count; i++)

...{
rval[i] = _dropDownLists[i].SelectedValue;
}
return rval;
}
}

[Browsable(false)]
public int[] SelectedIndexs

...{
get

...{
int[] rval = new int[_dropDownLists.Count];
for (int i = 0; i < _dropDownLists.Count; i++)

...{
rval[i] = _dropDownLists[i].SelectedIndex;
}
return rval;
}
}

[Browsable(true)]
[Category("Appearance")]
[Description("指定排列多个下拉列表的方向")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[TypeConverter(typeof(ExpandableObjectConverter))]
[NotifyParentProperty(true)]
public Style DropDownListStyle

...{
get

...{
return _dropDownListStyle;
}
}

#endregion


索引器#region 索引器

public DropDownList this[int index]

...{
get

...{
return _dropDownLists[index];
}
}

public DropDownList this[string name]

...{
get

...{
return this.FindControl(name) as DropDownList;
}
}

#endregion


Methods#region Methods

public override void DataBind()

...{
if (Direction == RepeatDirection.Horizontal)

...{
Literal l = new Literal();
l.Text = "<tr>";
this.Controls.Add(l);
}

if (_dataSource != null && _dataSource.Length != 0)

...{
Page.ClientScript.RegisterClientScriptBlock(typeof(CascadeDropdownLists), "refreshNext", refreshNext, true);
Page.ClientScript.RegisterClientScriptBlock(typeof(CascadeDropdownLists), "refreshNextForPostBack", refreshNextForPostBack, true);
Page.ClientScript.RegisterClientScriptBlock(typeof(CascadeDropdownLists), string.Concat("initCDDL", this.UniqueID), initCDDL, true);

for (int i = 0; i < _dataSource.Length; i++)

...{
DataTable dt = _dataSource[i];
if (dt.Rows.Count == 0)

...{
if (i == 0)

...{ //如果第一个表就是空的
Literal l = new Literal();
l.Text = "<td></td>";
this.Controls.Add(l);
}
break;
}

Literal left = new Literal();
this.Controls.Add(left);
if (Direction == RepeatDirection.Horizontal)

...{
left.Text = "<td>";
}
else

...{
left.Text = "<tr><td>";
}

DropDownList ddl = new DropDownList();
ddl.ID = string.Concat("ddl_", i.ToString());
this.Controls.Add(ddl);
this._dropDownLists.Add(ddl);
ddl.ControlStyle.CopyFrom(_dropDownListStyle);

for (int j = 0; j < dt.Rows.Count; j++)

...{
ListItem li = new ListItem();
li.Value = dt.Rows[j][0].ToString();
li.Text = dt.Rows[j][1].ToString();
if (i != 0)

...{
li.Attributes["ParentPtr"] = dt.Rows[j][2].ToString();
}

ddl.Items.Add(li);
}

Literal right = new Literal();
this.Controls.Add(right);
if (Direction == RepeatDirection.Horizontal)

...{
right.Text = "</td>";
}
else

...{
right.Text = "</td></tr>";
}

Page.ClientScript.RegisterStartupScript(typeof(CascadeDropdownLists), i.ToString(), string.Concat(initCDDL_ref + "(document.getElementById('", ddl.ClientID, "'));"), true);
ddl.Attributes["onchange"] = refreshNext_ref + "(document.getElementById('" + ddl.ClientID + "'))";
}
}

if (Direction == RepeatDirection.Horizontal)

...{
Literal l = new Literal();
l.Text = "</tr>";
this.Controls.Add(l);
}

base.DataBind();
}

protected override void OnPreRender(EventArgs e)

...{
if (this.Controls.Count == 0)

...{
Literal l = new Literal();
l.Text = "<td></td>";
this.Controls.Add(l);
}
else

...{
ViewState["DataSource"] = _dataSource;
if (!Page.IsPostBack)

...{
Page.ClientScript.RegisterStartupScript(typeof(CascadeDropdownLists), this.ClientID, refreshNext_ref + "(document.getElementById('" + _dropDownLists[0].ClientID + "'));", true);
}
else

...{
Page.ClientScript.RegisterStartupScript(typeof(CascadeDropdownLists), this.ClientID, refreshNextForPostBack_ref + "(document.getElementById('" + _dropDownLists[0].ClientID + "'));", true);
}
}

base.OnPreRender(e);
}

protected override void LoadViewState(object savedState)

...{
base.LoadViewState(savedState);

//回传状态还原
_dataSource = ViewState["DataSource"] as DataTable[];
DataBind();
}

#endregion


Fields#region Fields

private DataTable[] _dataSource;
private List<DropDownList> _dropDownLists = new List<DropDownList>();
private Style _dropDownListStyle = new Style();
private static string initCDDL;
private static string initCDDL_ref;
private static string refreshNext;
private static string refreshNext_ref;
private static string refreshNextForPostBack_ref;
private static string refreshNextForPostBack;

#endregion
static CascadeDropdownLists()

...{
refreshNext_ref = string.Concat("zhyFunc_", typeof(CascadeDropdownLists).FullName.Replace('.', 'I'), "_refreshNext");
initCDDL_ref = string.Concat("zhyFunc_", typeof(CascadeDropdownLists).FullName.Replace('.', 'I'), "_initCDDL");
refreshNextForPostBack_ref = string.Concat("zhyFunc_", typeof(CascadeDropdownLists).FullName.Replace('.', 'I'), "_refreshNextForPostBack");

refreshNext = string.Concat("function ", refreshNext_ref, @"(list)
{
var nextList;
try
{
var index = list.id.lastIndexOf('_');
var front = list.id.substring(0,index+1);
var count = list.id.substring(index+1);
var count2 = parseInt(count)+1;
nextList = document.getElementById(front + count2);
if(!nextList)
{
return;
}
}
catch(e)
{ //没有下一级
return;
}

while(nextList.options.length!=0)
{ //移除下级下拉列表的所有item
nextList.options.remove(0);
}

for(var i=0;i<nextList.bakOptions.length;i++)
{
if(nextList.bakOptions[i].ParentPtr == list.options[list.selectedIndex].value)
{
nextList.options.add(nextList.bakOptions[i]);
}
}
nextList.selectedIndex=0;" + refreshNext_ref + @"(nextList);
}
");
refreshNextForPostBack = string.Concat("function ", refreshNextForPostBack_ref, @"(list)
{
var nextList;
try
{
var index = list.id.lastIndexOf('_');
var front = list.id.substring(0,index+1);
var count = list.id.substring(index+1);
var count2 = parseInt(count)+1;
nextList = document.getElementById(front + count2);
if(!nextList)
{
return;
}
}
catch(e)
{ //没有下一级
return;
}

for(var i=0;i<nextList.options.length;)
{
if(nextList.options[i].ParentPtr != list.options[list.selectedIndex].value)
{
nextList.options.remove(i);
}
else
{
i++;
}
}" + refreshNextForPostBack_ref + @"(nextList);
}
");

initCDDL = string.Concat("function ", initCDDL_ref, @"(list)
{
list.bakOptions = new Array();
for(var i=0; i<list.options.length;i++)
{ //保存所有item
list.bakOptions.push(list.options.item(i));
}
}
");
}
}

}