效果:
避免了选择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));
}
}
");
}
}
}
本文介绍了一个简单的级联DropdownList自定义控件,该控件避免了选择时页面刷新或ajax延迟,提供了良好的用户体验。通过使用数据库脚本创建数据,然后在ASP.NET中进行数据绑定。控件通过DataTable数组分别存储tbl1和tbl2表的数据,并在客户端通过脚本控制显示。控件的状态保持是实现中的难点,同时具备快速显示和样式控制的优点,但依赖ViewState并需要改进。

2139

被折叠的 条评论
为什么被折叠?



