自己好几个地方要用到联动控件,项目又不想用Ajax,又想在客户端实现联动而不刷新,还想在服务器端用相似的代码来绑定项,还想在服务器端用相同的代码得到选定的值(也就是值可以提交),想了想,还是写一个控件好了.
以国家,省,市举例,我的实现思路是,
1.把所有国家绑定到国家列表,把所有的省绑定在省列表(包括各各国家),把所以的市绑定在市列表(包括所有的市).
2. 在客户端,用脚本把省,市和各项分组并保存,然后从 select.options 里去除不需要的option.
修正不能在同页面有多个联动的BUG
代码如下:
1. 控件代码:
namespace Iyond.Web.UI.WebControls


{
public class RelationDropDownList : System.Web.UI.WebControls.DropDownList

{
protected ValueParentValueDictionary valueParentValues = new ValueParentValueDictionary();

[DescriptionAttribute("ParentDropDownListControl"), IDReferenceProperty, TypeConverter(typeof(RelationDropDownListControlConverter)), Themeable(false), DefaultValue(""), CategoryAttribute("Behavior")]
public string ParentDropDownListControl

{
get

{
object obj1 = this.ViewState["ParentDropDownListControl"];
if (obj1 != null)

{
return (string)obj1;
}
return string.Empty;
}
set

{
this.ViewState["ParentDropDownListControl"] = value;
}
}

[DescriptionAttribute("ParentDropDownListControl"), IDReferenceProperty, TypeConverter(typeof(RelationDropDownListControlConverter)), Themeable(false), DefaultValue(""), CategoryAttribute("Behavior")]
public string ChildDropDownListControl

{
get

{
object obj1 = this.ViewState["ChildDropDownListControl"];
if (obj1 != null)

{
return (string)obj1;
}
return string.Empty;
}
set

{
this.ViewState["ChildDropDownListControl"] = value;
}
}


[DescriptionAttribute("DataParentValueField"), Themeable(false), DefaultValue(""), CategoryAttribute("Data")]
public virtual string DataParentValueField

{
get

{
object obj2 = this.ViewState["ParentDataValueField"];
if (obj2 != null)

{
return (string)obj2;
}
return string.Empty;
}
set

{
this.ViewState["ParentDataValueField"] = value;
if (base.Initialized)

{
base.RequiresDataBinding = true;
}
}
}



protected RelationDropDownList ParentDropDownList

{
get

{
if (string.IsNullOrEmpty(this.ParentDropDownListControl))
return null;

return this.NamingContainer.FindControl(this.ParentDropDownListControl) as RelationDropDownList;
}
}

protected RelationDropDownList ChildDropDownList

{
get

{
if (string.IsNullOrEmpty(this.ChildDropDownListControl))
return null;

return this.NamingContainer.FindControl(this.ChildDropDownListControl) as RelationDropDownList;
}
}

protected override void Render(HtmlTextWriter writer)

{

base.Render(writer);

if (this.ChildDropDownList == null)

{
//这是最后一级,依本身的SelectedIndex 来更新上级 修正不能在同页面有多个联动的BUG
Page.ClientScript.RegisterStartupScript(this.GetType(), this.ClientID + "_relddl_clearUp", string.Format("javascript:relddl_clearUp('{0}');", this.ClientID),true);
}
}

protected override void OnPreRender(EventArgs e)

{
Page.ClientScript.RegisterClientScriptResource(typeof(RelactionDropDownList), "Iyond.Web.UI.WebControls.DropDownList.Resources.RelationDDL.js");
base.OnPreRender(e);
}

protected override void AddAttributesToRender(HtmlTextWriter writer)

{
base.AutoPostBack = false;
base.AddAttributesToRender(writer);

writer.AddAttribute("parentSelect", this.ParentDropDownList != null?this.ParentDropDownList.ClientID:"");
writer.AddAttribute("childSelect",this.ChildDropDownList != null?this.ChildDropDownList.ClientID: "");


writer.AddAttribute( HtmlTextWriterAttribute.Onchange, "javascript:relddl_onchange(this,event)");
}

protected override void PerformDataBinding(System.Collections.IEnumerable dataSource)

{
base.PerformDataBinding(dataSource);
if (this.ParentDropDownList != null)

{
valueParentValues.Clear();

foreach (object obj in dataSource)

{
string dataValueField = this.DataValueField;
string parentDataValueField = this.DataParentValueField;
string value = Convert.ToString(DataBinder.GetPropertyValue(obj, dataValueField));
if (valueParentValues.ContainsKey(value))

{
throw new ArgumentException("相关的列表中. DataValueField 字段的值不能重复");
}


string parentValue = Convert.ToString(DataBinder.GetPropertyValue(obj, parentDataValueField));
valueParentValues.Add(value, parentValue);
}

SetItemParentValue();
}

}

protected virtual void SetItemParentValue()

{
foreach (string value in valueParentValues.Keys)

{
string parentValue = valueParentValues[value];

ListItem lt = this.Items.FindByValue(value);
if (lt != null)

{
lt.Attributes["parentValue"] =parentValue;
}
}
}



protected override object SaveViewState()

{
object x = base.SaveViewState();
object y = valueParentValues;
object z = null;
if (((z == null) && (y == null)))

{
return null;
}
return new Triplet(x, y, z);
}

protected override void LoadViewState(object savedState)

{
if (savedState != null)

{
Triplet triplet = (Triplet)savedState;
base.LoadViewState(triplet.First);
valueParentValues = triplet.Second as ValueParentValueDictionary;
if (valueParentValues != null)

{
SetItemParentValue();
}
}
else

{
base.LoadViewState(null);
}
}

}

public class RelationDropDownListControlConverter : ControlIDConverter

{
protected override bool FilterControl(Control control)

{
if (control is RelationDropDownList)
return true;
else

{
return false;
}

}
}

[Serializable]
public class ValueParentValueDictionary : Dictionary<string, string>

{
public ValueParentValueDictionary()
:base()

{}

public ValueParentValueDictionary(IDictionary<string, string> dictionary)
:base(dictionary)

{}
public ValueParentValueDictionary(IEqualityComparer<string> comparer)
:base(comparer)

{}

public ValueParentValueDictionary(int capacity)
:base(capacity)

{}

public ValueParentValueDictionary(IDictionary<string, string> dictionary, IEqualityComparer<string> comparer)
:base(dictionary,comparer)

{}
public ValueParentValueDictionary(int capacity, IEqualityComparer<string> comparer)
:base(capacity,comparer)

{}
protected ValueParentValueDictionary(SerializationInfo info, StreamingContext context)
:base(info,context)

{}
}

}

2. 使用举例:
<iyond:RelationDropDownList ID="ddlCountry" runat="server" ChildDropDownListControl="ddlProvince">
</iyond:RelationDropDownList>
<iyond:RelationDropDownList ID="ddlProvince" runat="server" ChildDropDownListControl="ddlCity" ParentDropDownListControl="ddlCountry">
</iyond:RelationDropDownList>
<iyond:RelationDropDownList ID="ddlCity" runat="server" ParentDropDownListControl="ddlProvince">
</iyond:RelationDropDownList>

protected void Page_Load(object sender, EventArgs e)

{
if (!Page.IsPostBack)

{
List<ZoneItem> listCountry = new List<ZoneItem>(

new ZoneItem[]
{
new ZoneItem(1,"中国"),
new ZoneItem(2,"美国")});

List<ZoneItem> listProvince = new List<ZoneItem>(

new ZoneItem[]
{
new ZoneItem(1,"河北省",1),
new ZoneItem(2,"河南省",1),
new ZoneItem(3,"加州",2)
});


List<ZoneItem> listCity = new List<ZoneItem>(

new ZoneItem[]
{
new ZoneItem(1,"石家庄",1),
new ZoneItem(2,"邯郸",1),
new ZoneItem(3,"安阳",2),
new ZoneItem(4,"纽约",3)
});


this.ddlCountry.DataSource = listCountry;
this.ddlCountry.DataTextField = "Name";
this.ddlCountry.DataValueField = "Id";
this.ddlCountry.DataParentValueField = "ParentId";
this.ddlCountry.DataBind();

this.ddlProvince.DataSource = listProvince;
this.ddlProvince.DataTextField = "Name";
this.ddlProvince.DataValueField = "Id";
this.ddlProvince.DataParentValueField = "ParentId";
this.ddlProvince.DataBind();

this.ddlCity.DataSource = listCity;
this.ddlCity.DataTextField = "Name";
this.ddlCity.DataValueField = "Id";
this.ddlCity.DataParentValueField = "ParentId";
this.ddlCity.DataBind();

}
else

{
Response.Write(this.ddlCity.SelectedValue);
}

}

3.生成的客户页HTML
<select name="ddlCountry" id="ddlCountry" parentSelect="" childSelect="ddlProvince" onchange="javascript:relddl_onchange(this,event)">
<option selected="selected" value="1">中国</option>
<option value="2">美国</option>

</select>
<select name="ddlProvince" id="ddlProvince" parentSelect="ddlCountry" childSelect="ddlCity" onchange="javascript:relddl_onchange(this,event)">
<option selected="selected" value="1" parentValue="1">河北省</option>
<option value="2" parentValue="1">河南省</option>
<option value="3" parentValue="2">加州</option>

</select>
<select name="ddlCity" id="ddlCity" parentSelect="ddlProvince" childSelect="" onchange="javascript:relddl_onchange(this,event)">
<option selected="selected" value="1" parentValue="1">石家庄</option>
<option value="2" parentValue="1">邯郸</option>
<option value="3" parentValue="2">安阳</option>
<option value="4" parentValue="3">纽约</option>

</select>

丢了JS了:
//======================================================================
// Cutech DropDownList Script
// Version 1.0 Code by Evlon.(evlon@126.com)
// Copyright(C)2007-2008 Cutech Co,ltd. All Rights Reserved.
//=======================================================================
function relddl_onchange(sender, e)

{
var selId = sender.childSelect;
if(selId == '')
return;
var selectedValue = sender.options[sender.selectedIndex].value;
var sel = document.getElementById(selId);
if(sel != null)

{
with(sel)

{
//保存选定值
if(currentGroupItem != null)

{
currentGroupItem[0] = options.selectedIndex;
}
var items = groupItems.Item(selectedValue);
currentGroupItem = items;
options.length = 0;
if(items != null)

{
if(items.length > 1)

{
for(var i = 1; i < items.length; ++i)

{
options.add(items[i]);
}
options.selectedIndex = items[0] == -1?0:items[0];
if(options.selectedIndex != -1)

{
var e = document.createEventObject();
fireEvent("onchange");
}
}
else

{
disable = true;
}
}
}
}
}
function relddl_setSelectedValue(selElement, selectedValue)

{
with(selElement.options)

{
for(var i = 0; i < length; ++i)

{
var lt = item(i);
if(lt.value == selectedValue)

{
lt.selected = true;
return lt.index;
}
}
}
}
function relddl_initSelect(selElement)

{
var removeIndex = [];
//分组
with(selElement.options)

{
var dic = new ActiveXObject("Scripting.Dictionary");
var selectedParentValue = item(selectedIndex).parentValue;
setAttribute("groupItems",dic);
setAttribute("currentGroupItem",null);
for(var i = 0; i < length; ++i)

{
var lt = item(i);
if(!dic.Exists(lt.parentValue))

{
if(selectedIndex == i)

{
currentGroupItem = [selectedIndex];
dic.Add(lt.parentValue,currentGroupItem);
}
else

{
dic.Add(lt.parentValue,[-1]);
}
}
dic.Item(lt.parentValue).push(lt);
if(lt.parentValue != selectedParentValue)

{
removeIndex.push(lt.index);
}
}

while(removeIndex.length > 0)

{
remove(removeIndex.pop());
}
}
}

function relddl_clearUp(selectId)

{
var selId = selectId;
var selectedValue = '';
for(var i = 0;selId != ''; ++i)

{
var sel = document.getElementById(selId);
if(sel == null)
return ;
if(i > 0)

{
var selectedIndex = relddl_setSelectedValue(sel,selectedValue);
if(selectedIndex == -1)
return;
}
relddl_initSelect(sel);
if(sel.options.length > 0)

{
selectedValue = sel.options[0].parentValue;
if(selectedValue == '')
return;
}
selId = sel.parentSelect;
}
}

最后.欢迎大家拍砖.