简单好用的联动下拉控件(修正)
自己好几个地方要用到联动控件,项目又不想用Ajax,又想在客户端实现联动而不刷新,还想在服务器端用相似的代码来绑定项,还想在服务器端用相同的代码得到选定的值(也就是值可以提交),想了想,还是写一个控件好了.
以国家,省,市举例,我的实现思路是,
1.把所有国家绑定到国家列表,把所有的省绑定在省列表(包括各各国家),把所以的市绑定在市列表(包括所有的市).
2. 在客户端,用脚本把省,市和各项分组并保存,然后从 select.options 里去除不需要的option.
修正不能在同页面有多个联动的BUG
代码如下:
1. 控件代码:
丢了JS了:
最后.欢迎大家拍砖.
以国家,省,市举例,我的实现思路是,
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. 使用举例:{
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)
{}
}
}
<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>
</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{
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);
}
}
<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>
<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;
}
}
// 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;
}
}
最后.欢迎大家拍砖.
QQ:273352165
evlon#126.com
转载请注明出处。