DropDownList下拉框多选

最近弄完个项目、项目需要支持多选功能、找了很多例子没找到合适的,最后自己开发了个控件:

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();
            }
        }

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 }


在预呈现 OnPreRender 事件里面注册客户端脚本:

protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (!DesignMode)
            {
                DoRegisterScript();
            }
        }

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);


重写 TagKey 设置为:Table

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); }
        }

AddCustomAttribute
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 的宽度。

GetDivWidth()
 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);
        }

RenderCustomContent
 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("<br/>");
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 的内容:

ModifyRenderedCheckboxes
  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 }


在 LoadPostData 事件里面获取选择的内容:

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);

responseOnFormClick
 1 //<!--检测当前点击是否在弹出的DIV 范围;否则隐藏弹出的DIV-->
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

toggleDivShowState
 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 时设置上次选择的值

selectDefaultItem
 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 冲突。

点击下载本例源码

 

转自:http://www.soaspx.com/dotnet/asp.net/Control/control_20111011_8134.html

posted @ 2012-05-03 15:36  小草旁的大树  阅读(8708)  评论(5编辑  收藏  举报