最近弄完个项目、项目需要支持多选功能、找了很多例子没找到合适的,最后自己开发了个控件:
DropDownCheckBoxList 控件继承 DropDownList ;
整个控件由四部分组成:一个文本框、两个图标(向下|向上)、一个隐藏的 DIV 、两个隐藏域。控件示意图
收缩状态:
展开状态:
先介绍些关键属性:
1. DisplayMode 有两个值 Label,Value;分别表示显示文本、显示值。
2. Splitor 多选时,多个值间的分隔符。
3. ShowSelectAllOption 是否显示" 全选 " 选项、一般多项选择都会有个" 全选 " 功能。
4. SelectAllOptionLabel 全选选项显示的文本,默认值:"全选"。
重写了 OnInit 事件,在 OnInit 事件里面创建控件:
protected override void OnInit(EventArgs e)
{ base.OnInit(e); if (!DesignMode) { CreateControls(); } }1 protected void CreateControls() 2 { 3 txt = new TextBox(); 4 txt.ID = this.ClientID + "_txtMain"; 5 txt.ReadOnly = true; 6 txt.Width = (Unit)(this.Width.Value - 20); 7 txt.Style.Add(HtmlTextWriterStyle.Padding, "0"); 8 txt.Style.Add(HtmlTextWriterStyle.Margin, "0"); 9 txt.Height = this.Height; 10 11 if (txt.Height.IsEmpty) 12 { txt.Height = 15; } 13 14 hfValueText = new HiddenField(); 15 hfValueText.ID = string.Format("{0}_{1}", this.ClientID, "selectItemValueText"); 16 hfValue = new HiddenField(); 17 hfValue.ID = string.Format("{0}_{1}", this.ClientID, "selectItemValue"); 18 19 txt.Enabled = this.Enabled; 20 }
protected override void OnPreRender(EventArgs e)
{ base.OnPreRender(e); if (!DesignMode) { DoRegisterScript(); } }1 ClientScriptManager sc = this.Page.Page.ClientScript; 2 3 string scriptString = sc.GetWebResourceUrl(this.GetType(), "DevControl.Resources.DropDownCheckBoxList.js"); 4 5 if (!sc.IsClientScriptIncludeRegistered("DropDownGridScriptKey")) 6 { sc.RegisterClientScriptInclude(this.GetType(), "DropDownGridScriptKey", scriptString); } 7 8 scriptString = Page.Form.Attributes["onclick"]; 9 if (string.IsNullOrEmpty(scriptString)) 10 { scriptString = string.Format("responseOnFormClick(event,'{0}');", this.ClientID); } 11 else 12 { scriptString = string.Format("{0} responseOnFormClick(event,'{1}');", scriptString, this.ClientID); } 13 14 Page.Form.Attributes.Add("onclick", scriptString); 15 16 this.Page.RegisterRequiresPostBack(this);
protected override HtmlTextWriterTag TagKey
{ get { if (!DesignMode) { return HtmlTextWriterTag.Table; } return base.TagKey; } }在 AddAttributesToRender 事件中 设置 table 的属性
protected override void AddAttributesToRender(HtmlTextWriter writer)
{ if (!DesignMode) { AddCustomAttribute(writer); } else { base.AddAttributesToRender(writer); } }1 writer.AddAttribute(HtmlTextWriterAttribute.Width, this.Width.ToString()); 2 writer.AddAttribute(HtmlTextWriterAttribute.Border, "0"); 3 writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_displaytable"); 4 writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0"); 5 writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0"); 6 writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative"); 7 writer.AddStyleAttribute(HtmlTextWriterStyle.ZIndex, "1"); 8 writer.AddStyleAttribute(HtmlTextWriterStyle.Left, "-5px");
在 GetDivWidth 方法里面动态算出 DIV 宽度,避免当某项内容长度超出 DIV 的默认宽度时,动态调整DIV 的宽度。
1 int itemWidth = 0; 2 int byteCount = 0; 3 foreach (ListItem item in this.Items) 4 { 5 byteCount = System.Text.UnicodeEncoding.Default.GetByteCount(item.Text); 6 if (byteCount > itemWidth) 7 { itemWidth = byteCount; } 8 } 9 10 itemWidth = itemWidth * 8 + 20; 11 itemWidth = itemWidth + 16; //加上checkbox 宽度 12 13 if (itemWidth > this.Width.Value) 14 { this.Width = itemWidth; }
在 RenderContents 事件里面呈现默认显示(文本框、图标)的内容:
protected override void RenderContents(HtmlTextWriter writer)
{ if (DesignMode) { base.RenderContents(writer); return; } RenderCustomContent(writer); }1 void RenderCustomContent(HtmlTextWriter writer) 2 { 3 GetDivWidth(); 4 string divId = this.ClientID + "_div"; 5 string cmbDown = this.ClientID + "_imgDown"; 6 string cmbUp = this.ClientID + "_imgUp"; 7 8 // base.RenderContents(writer); 9 ////控件显示主体 10 writer.AddAttribute(HtmlTextWriterAttribute.Align, "left"); 11 writer.RenderBeginTag(HtmlTextWriterTag.Tr); 12 writer.RenderBeginTag(HtmlTextWriterTag.Td); 13 writer.AddStyleAttribute(HtmlTextWriterStyle.Cursor, "default"); 14 if (this.Enabled) 15 { writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("toggleDivShowState('{0}','{1}','{2}');", this.ClientID, Splitor, this.Items.Count)); } 16 ////控件主体文本框 17 txt.RenderControl(writer); 18 19 writer.Write(""); 20 ////隐藏下拉面板 21 writer.AddAttribute(HtmlTextWriterAttribute.Id, divId); 22 writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none"); 23 writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, "#ECECE3"); 24 writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "absolute"); 25 writer.AddStyleAttribute(HtmlTextWriterStyle.ZIndex, "32766"); 26 writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, "Gray"); 27 writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "thin"); 28 writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "double"); 29 writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "top"); 30 writer.AddStyleAttribute(HtmlTextWriterStyle.Width, this.Width.ToString()); 31 writer.RenderBeginTag(HtmlTextWriterTag.Div); 32 ////呈现 item 列表 33 ModifyRenderedCheckboxes(writer); 34 writer.RenderEndTag();//end div 35 36 writer.RenderEndTag();//end td 37 ////下拉图标 38 writer.AddAttribute(HtmlTextWriterAttribute.Border, "0"); 39 writer.RenderBeginTag(HtmlTextWriterTag.Td); 40 writer.AddStyleAttribute(HtmlTextWriterStyle.Margin, "0"); 41 writer.AddStyleAttribute(HtmlTextWriterStyle.Padding, "0"); 42 writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative"); 43 writer.AddStyleAttribute(HtmlTextWriterStyle.Left, "-8px"); 44 writer.AddAttribute(HtmlTextWriterAttribute.Src, this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "DevControl.Resources.cmb_down.jpg")); 45 46 if (this.Enabled) 47 { writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("toggleDivShowState('{0}','{1}','{2}');", this.ClientID, Splitor, this.Items.Count)); } 48 49 writer.AddAttribute(HtmlTextWriterAttribute.Id, cmbDown); 50 writer.RenderBeginTag(HtmlTextWriterTag.Img); 51 writer.RenderEndTag();//end img 52 writer.AddStyleAttribute(HtmlTextWriterStyle.Margin, "0"); 53 writer.AddStyleAttribute(HtmlTextWriterStyle.Padding, "0"); 54 writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative"); 55 writer.AddStyleAttribute(HtmlTextWriterStyle.Left, "-8px"); 56 writer.AddAttribute(HtmlTextWriterAttribute.Src, this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "DevControl.Resources.cmb_up.jpg")); 57 if (this.Enabled) 58 { writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("toggleDivShowState('{0}','{1}','{2}');", this.ClientID, Splitor, this.Items.Count)); } 59 writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none"); 60 writer.AddAttribute(HtmlTextWriterAttribute.Id, cmbUp); 61 writer.RenderBeginTag(HtmlTextWriterTag.Img); 62 writer.RenderEndTag();//end img 63 64 writer.RenderEndTag();//end td 65 writer.RenderEndTag();//end tr 66 }
在 ModifyRenderedCheckboxes 方法里面呈现 DIV 的内容:
1 protected void ModifyRenderedCheckboxes(HtmlTextWriter writer) 2 { 3 int index = 0; 4 5 string spanId = ""; 6 string wapperId = ""; 7 string allChkId = ""; 8 9 writer.AddAttribute(HtmlTextWriterAttribute.Width, this.Width.ToString()); 10 writer.AddAttribute(HtmlTextWriterAttribute.Border, "0"); 11 writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_Optiontable"); 12 writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0"); 13 writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0"); 14 writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative"); 15 writer.AddStyleAttribute(HtmlTextWriterStyle.Left, "0px"); 16 writer.RenderBeginTag(HtmlTextWriterTag.Table); 17 18 #region 首选项 19 if (ShowSelectAllOption) 20 { 21 writer.RenderBeginTag(HtmlTextWriterTag.Tr); 22 wapperId = string.Format("{0}_{1}", this.ClientID, "chkAllItemWapper"); 23 allChkId = string.Format("{0}_{1}", this.ClientID, "chkAllItemValue"); 24 spanId = string.Format("{0}_{1}", this.ClientID, "chkAllItemText"); 25 26 writer.AddAttribute("onmouseover", string.Format("setStyleOnMouseOver('{0}');", wapperId)); 27 writer.AddAttribute("onmouseout", string.Format("setStyleOnMouseOut('{0}');", wapperId)); 28 29 writer.AddStyleAttribute(HtmlTextWriterStyle.Width, (this.Width.Value - 10).ToString()); 30 writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "20px"); 31 writer.AddAttribute(HtmlTextWriterAttribute.Id, wapperId); 32 writer.AddStyleAttribute("cursor", "pointer"); 33 writer.AddStyleAttribute("cursor", "hand"); 34 writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "middle"); 35 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingLeft, "5px"); 36 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingRight, "5px"); 37 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("return clickChangeValueWhenCheckAllCheckBox(event,'{0}','{1}');", allChkId, this.ClientID)); 38 writer.RenderBeginTag(HtmlTextWriterTag.Td); 39 writer.AddAttribute(HtmlTextWriterAttribute.Id, allChkId); 40 writer.AddStyleAttribute("cursor", "pointer"); 41 writer.AddStyleAttribute("cursor", "hand"); 42 writer.AddAttribute(HtmlTextWriterAttribute.Value, ""); 43 writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "middle"); 44 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingBottom, "5px"); 45 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("return mouseUpdateValueWhenSelectAllStateChanged(event,this,'{0}');", this.ClientID)); 46 writer.AddAttribute(HtmlTextWriterAttribute.Type, "checkbox"); 47 writer.RenderBeginTag(HtmlTextWriterTag.Input); 48 writer.RenderEndTag();//end input 49 50 writer.AddAttribute(HtmlTextWriterAttribute.Id, spanId); 51 writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "small"); 52 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingTop, "5px"); 53 writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "middle"); 54 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingBottom, "5px"); 55 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("return mouseUpdateValueWhenCheckAllCheckBox(event,'{0}','{1}');", allChkId, this.ClientID)); 56 writer.RenderBeginTag(HtmlTextWriterTag.Span); 57 writer.Write(SelectAllOptionLabel); 58 writer.RenderEndTag();//end span 59 60 writer.RenderEndTag();//td 61 writer.RenderEndTag();//tr 62 } 63 #endregion 64 65 #region 内容选项 66 string chkId = ""; 67 foreach (ListItem item in this.Items) 68 { 69 writer.RenderBeginTag(HtmlTextWriterTag.Tr); 70 wapperId = string.Format("{0}_{1}{2}", this.ClientID, "chkItemWapper", index.ToString()); 71 chkId = string.Format("{0}_{1}{2}", this.ClientID, "chkItemValue", index.ToString()); 72 spanId = string.Format("{0}_{1}{2}", this.ClientID, "chkItemText", index.ToString()); 73 74 writer.AddAttribute("onmouseover", string.Format("setStyleOnMouseOver('{0}');", wapperId)); 75 writer.AddAttribute("onmouseout", string.Format("setStyleOnMouseOut('{0}');", wapperId)); 76 77 writer.AddStyleAttribute(HtmlTextWriterStyle.Width, (this.Width.Value - 10).ToString()); 78 writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "20px"); 79 writer.AddAttribute(HtmlTextWriterAttribute.Id, wapperId); 80 writer.AddStyleAttribute("cursor", "pointer"); 81 writer.AddStyleAttribute("cursor", "hand"); 82 writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "middle"); 83 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingLeft, "5px"); 84 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingRight, "5px"); 85 86 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("return clickChangeValueWhenCheckItemCheckBox(event,'{0}','{1}','{2}','{3}');", chkId, spanId, allChkId, this.ClientID)); 87 writer.RenderBeginTag(HtmlTextWriterTag.Td); 88 writer.AddAttribute(HtmlTextWriterAttribute.Id, chkId); 89 writer.AddStyleAttribute("cursor", "pointer"); 90 writer.AddStyleAttribute("cursor", "hand"); 91 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingTop, "5px"); 92 writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "middle"); 93 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingBottom, "5px"); 94 writer.AddAttribute(HtmlTextWriterAttribute.Width, "16px"); 95 writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value); 96 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("return mouseUpdateValueWhenCheckItemStateChanged(event,'{0}','{1}','{2}','{3}');", chkId, spanId, allChkId, this.ClientID)); 97 writer.AddAttribute(HtmlTextWriterAttribute.Type, "checkbox"); 98 writer.RenderBeginTag(HtmlTextWriterTag.Input); 99 writer.RenderEndTag();//end input 100 101 writer.AddAttribute(HtmlTextWriterAttribute.Id, spanId); 102 writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "small"); 103 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingTop, "5px"); 104 writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "middle"); 105 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingBottom, "5px"); 106 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, string.Format("return mouseUpdateValueWhenCheckItemCheckBox(event,'{0}','{1}','{2}','{3}');", chkId, spanId, allChkId, this.ClientID)); 107 writer.RenderBeginTag(HtmlTextWriterTag.Span); 108 if (DisplayMode == DevControl.DisplayMode.Label) 109 { writer.Write(item.Text); } 110 else 111 { writer.Write(item.Value); } 112 113 writer.RenderEndTag();//end span 114 115 writer.RenderEndTag();//td 116 writer.RenderEndTag();//tr 117 index++; 118 } 119 #endregion 120 121 writer.RenderBeginTag(HtmlTextWriterTag.Tr); 122 writer.RenderBeginTag(HtmlTextWriterTag.Td); 123 hfValue.RenderControl(writer); 124 hfValueText.RenderControl(writer); 125 writer.RenderEndTag();//end td 126 writer.RenderEndTag();//end tr 127 writer.RenderEndTag();//end table 128 }
protected override bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{ if (!DesignMode) { this.txt.Text = postCollection[txt.ID]; this.hfValue.Value = postCollection[hfValue.ID]; this.hfValueText.Value = postCollection[hfValueText.ID]; return true; } return base.LoadPostData(postDataKey, postCollection); }定义个枚举类型 DisplayMode 表示显示"文本或值"
[Serializable]
public enum DisplayMode { /// <summary> /// 显示文本 /// </summary> Label, /// <summary> /// 显示值 /// </summary> Value }客户端脚本 DropDownCheckBoxList.js 利用 JQuery 来处理:
扩展了 Array :
Array.prototype.initItem
Array.prototype.hasItem
Array.prototype.addItem
Array.prototype.removeItem
Array.prototype.joinItem
函数responseOnFormClick 检测当前点击是否在弹出的DIV 范围;否则隐藏弹出的DIV
function (e, prefix);1 // 2 function responseOnFormClick(e, prefix) { 3 idPrefix = prefix; 4 $divObj = $("#" + idPrefix + "_div"); 5 if ($divObj.css("display") == "none") { 6 return false; 7 } 8 var $imgDownObj = $("#" + idPrefix + "_imgDown"); 9 var $imgUpObj = $("#" + idPrefix + "_imgUp"); 10 var targetId; 11 if ($.browser.msie) { targetId = e.srcElement.id; } 12 else { targetId = e.target.id; } 13 if (targetId == undefined || targetId.indexOf(idPrefix) < 0) { 14 $divObj.css("display", "none"); 15 $imgDownObj.css("display", "inline"); 16 $imgUpObj.css("display", "none"); 17 } 18 return false; 19 }
函数 toggleDivShowState 显示或者隐藏弹出的DIV
1 ////显示或者隐藏弹出的DIV 2 function toggleDivShowState(prefix, splitorString, iCount) { 3 idPrefix = prefix; 4 splitor = splitorString; 5 itemCount = iCount; 6 vItems = new Array(iCount); 7 vItems.initItem(); 8 tItems = new Array(iCount); 9 tItems.initItem(); 10 vtItems = new Array(iCount); 11 vtItems.initItem(); 12 $hfTextObj = $("#" + idPrefix + "_selectItemValueText"); 13 $hfValueObj = $("#" + idPrefix + "_selectItemValue"); 14 $divObj = $("#" + idPrefix + "_div"); 15 $txtObj = $("#" + idPrefix + "_txtMain"); 16 setObjectState(idPrefix + "_div"); 17 if ($divObj.css("display") == "inline") { 18 selectDefaultItem(idPrefix + "_selectItemValue", idPrefix); 19 } 20 setObjectState(idPrefix + "_imgDown"); 21 setObjectState(idPrefix + "_imgUp"); 22 }
函数 selectDefaultItem 弹出DIV 时设置上次选择的值
1 function selectDefaultItem(hfValueId, prefix) { 2 $hfValueObj = $("#" + hfValueId); 3 if (!$hfValueObj.val()) 4 { return; } 5 var arr = $hfValueObj.val().split(splitor); 6 if (!arr) 7 { return; } 8 vItems.initItem(); 9 tItems.initItem(); 10 vtItems.initItem(); 11 var subNodes; 12 var firstNode; 13 var lastNode; 14 var nodeValue; 15 var nodeText; 16 var $tdNodes; 17 $divObj = $("#" + prefix + "_div"); 18 var $tableNodes = $divObj.children('table'); 19 var tableId = $tableNodes.get(0).id; 20 $("#" + tableId + " tr").each(function () { 21 $tdNodes = $(this).children('td'); 22 $tdNodes.each(function () { 23 subNodes = this; 24 firstNode = subNodes.children[0]; 25 lastNode = subNodes.children[1]; 26 nodeValue = firstNode.value; 27 if (nodeValue) { 28 if (lastNode.innerText) 29 { nodeText = lastNode.innerText; } 30 else 31 { nodeText = lastNode.textContent; } 32 if (arr.hasItem(nodeValue)) { 33 if (!vItems.hasItem(nodeValue)) 34 { vItems.addItem(nodeValue); } 35 if (!tItems.hasItem(nodeText)) 36 { tItems.addItem(nodeText); } 37 if (!vtItems.hasItem(nodeValue + "|" + nodeText)) 38 { vtItems.addItem(nodeValue + "|" + nodeText); } 39 if (!tItems.hasItem(nodeText)) 40 { tItems.addItem(nodeText); } 41 if (!vtItems.hasItem(nodeValue + "|" + nodeText)) 42 { vtItems.addItem(nodeValue + "|" + nodeText); } 43 firstNode.checked = true; 44 } 45 } 46 }); 47 }); 48 49 $txtObj.val(tItems.joinItem(splitor)); 50 $hfTextObj.val(vtItems.joinItem(splitor)); 51 $hfValueObj.val(vItems.joinItem(splitor)); 52 }
控件支持主流浏览器 IE 7+,FF3.5+,Chrome 9+,Safari 5 等浏览器。但在 IE6中与 与浏览器自身的 select 冲突。