ASP.NET: Custom AutoCompleteTextBox WebControl

作者:网络 来源:佚名 更新时间:2008-07-07 19:46:40 点击:

  这是一个teddy最近封装的autocompletetextbox。我们知道,asp.net本身的textbox也是支持一定的autocomplete功能的,但是那是依赖浏览器实现的,并不能指定自定义的autocomplete候选项。本文列举的autocompletetextbox则弥补了这个缺憾。只需设置autocompletetextbox.autocompletedata属性,传递一个string[],就能使textbox支持自定义候选项了。

  autocomplete逻辑

  如果没有匹配当前输入的候选项,则同一般的textbox;如果只有一个候选项与当前输入匹配,则自动完成;如果有超过一个候选项与当前输入匹配,则在textbox中自动完成第一个候选项,并弹出包含所有候选项的弹出框。

  实现源码

  源码是在vs2005编译的,不过实际上几乎没有使用依赖2.0的语法,在vs2003下经极少修改就同样能编译的。

using system;
using system.collections.generic;
using system.componentmodel;
using system.text;
using system.web;
using system.web.ui;
using system.web.ui.webcontrols;
using system.web.ui.htmlcontrols;

namespace ilungasoft.framework.web.ui.webcontrols
{
    [toolboxdata("<{0}:autocompletetextbox runat=server></{0}:autocompletetextbox>")]
    public class autocompletetextbox : webcontrol
    {
        private members#region private members

        private textbox textbox = new textbox();
        private htmlgenericcontrol autocompleteframe = new htmlgenericcontrol();

        private string tojsstringarray(params string[] strs)
        {
            if (strs != null && strs.length > 0)
            {
                stringbuilder sb = new stringbuilder();
                sb.append(" new array(");

                foreach (string str in strs)
                {
                    sb.append(string.format("'{0}', ", str.replace("'", "\\'")));
                }

                return sb.tostring().trimend(',', ' ') + ");";
            }
            else
            {
                return " new array;";
            }
        }

        private string makeuniqueid(string id)
        {
            if (id != null && id.trim().length > 0)
            {
                return string.concat(this.uniqueid.replace("$", "_"), "_", id);
            }
            else
            {
                return this.uniqueid.replace("$", "_");
            }
        }

        #endregion

        properties#region properties

        [bindable(true)]
        [category("appearance")]
        [defaultvalue("")]
        [localizable(true)]
        public string text
        {
            get
            {
                if (page.ispostback)
                {
                    textbox.text = page.request.form[makeuniqueid("textbox")];
                }
                return textbox.text;
            }
            set
            {
                textbox.text = value;
            }
        }

        [bindable(true)]
        [category("behavior")]
        [defaultvalue("")]
        [localizable(true)]
        public int maxlength
        {
            get
            {
                return textbox.maxlength;
            }
            set
            {
                textbox.maxlength = value;
            }
        }

        [bindable(true)]
        [category("behavior")]
        [defaultvalue(false)]
        [localizable(true)]
        public bool readonly
        {
            get
            {
                return textbox.readonly;
            }
            set
            {
                textbox.readonly = value;
            }
        }

        public string[] autocompletedata
        {
            get
            {
                string[] s = (string[])viewstate["autocompletedata"];
                return ((s == null) ? null : s);
            }
            set
            {
                viewstate["autocompletedata"] = value;
            }
        }

        #endregion

        overriden members#region overriden members

        protected override void createchildcontrols()
        {
            create textbox#region create textbox

            textbox.id = makeuniqueid("textbox");
            textbox.autocompletetype = autocompletetype.disabled;
            textbox.attributes.add("onkeypress", string.format("return __doautocomplete(event, '{0}')", makeuniqueid(null)));
            textbox.attributes.add("onblur", string.format("if (!document.show_{0}) document.getelementbyid('{1}').style.display = 'none';", makeuniqueid(null), makeuniqueid("autocompleteframe")));
            textbox.width = width;

            #endregion

            create autocompleteframe#region create autocompleteframe

            autocompleteframe.tagname = "iframe";
            autocompleteframe.id = makeuniqueid("autocompleteframe");
            autocompleteframe.attributes.add("style", "display:none; position: absolute; border: ridge 1px");
            autocompleteframe.attributes.add("frameborder", "0");
            autocompleteframe.attributes.add("marginheight", "0");
            autocompleteframe.attributes.add("marginwidth", "2");
            autocompleteframe.attributes.add("scrolling", "auto");
            autocompleteframe.attributes.add("width", width.tostring());
            autocompleteframe.attributes.add("height", "100px");
            autocompleteframe.attributes.add("src", "javascript:''");
            autocompleteframe.attributes.add("onmouseover", string.format("document.show_{0} = true;", makeuniqueid(null)));
            autocompleteframe.attributes.add("onmouseout", string.format("document.show_{0} = false;", makeuniqueid(null)));

            #endregion
        }

        protected override void onprerender(eventargs e)
        {
            register client script block#region register client script block

            if (!page.clientscript.isclientscriptblockregistered("__doautocomplete"))
            {
                string script = string.concat(
                    "<script language=\"javascript\" type=\"text/javascript\">\r\n",
                    "    var isopera = navigator.useragent.indexof('opera') > -1;\r\n",
                    "    var isie = navigator.useragent.indexof('msie') > 1 && !isopera;\r\n",
                    "    var ismoz = navigator.useragent.indexof('mozilla/5.') == 0 && !isopera;\r\n",
                    "\r\n",
                    "    function textboxselect (otextbox, istart, iend)\r\n",
                    "    {\r\n",
                    "       switch(arguments.length) {\r\n",
                    "           case 1:\r\n",
                    "               otextbox.select();\r\n",
                    "               break;\r\n",
                    "\r\n",
                    "           case 2:\r\n",
                    "               iend = otextbox.value.length;\r\n",
                    "               /* falls through */\r\n",
                    "               \r\n",
                    "           case 3:          \r\n",
                    "               if (isie) {\r\n",
                    "                   var orange = otextbox.createtextrange();\r\n",
                    "                   orange.movestart(\"character\", istart);\r\n",
                    "                   orange.moveend(\"character\", -otextbox.value.length + iend);      \r\n",
                    "                   orange.select();                                              \r\n",
                    "               } else if (ismoz){\r\n",
                    "                   otextbox.setselectionrange(istart, iend);\r\n",
                    "               }                    \r\n",
                    "       }\r\n",
                    "\r\n",
                    "       otextbox.focus();\r\n",
                    "    }\r\n",
                    "\r\n",
                    "    function textboxreplaceselect (otextbox, stext)\r\n",
                    "    {\r\n",
                    "       if (isie) {\r\n",
                    "           var orange = document.selection.createrange();\r\n",
                    "           orange.text = stext;\r\n",
                    "           orange.collapse(true);\r\n",
                    "           orange.select();                                \r\n",
                    "       } else if (ismoz) {\r\n",
                    "           var istart = otextbox.selectionstart;\r\n",
                    "           otextbox.value = otextbox.value.substring(0, istart) + stext + otextbox.value.substring(otextbox.selectionend, otextbox.value.length);\r\n",
                    "           otextbox.setselectionrange(istart + stext.length, istart + stext.length);\r\n",
                    "       }\r\n",
                    "\r\n",
                    "       otextbox.focus();\r\n",
                    "    }\r\n",
                    "\r\n",
                    "    function autocompletematch (stext, arrvalues)\r\n",
                    "    {\r\n",
                    "       var retmatches = \"\"; \r\n",
                    "       \r\n",
                    "       for (var i=0; i < arrvalues.length; i++)\r\n",
                    "       {\r\n",
                    "           if (arrvalues[i].indexof(stext) == 0)\r\n",
                    "           {\r\n",
                    "               retmatches += arrvalues[i] + ',';\r\n",
                    "           }\r\n",
                    "       }\r\n",
                    "      \r\n",
                    "      if (retmatches.length > 0)\r\n",
                    "      {\r\n",
                    "          retmatches = retmatches.substr(0, retmatches.length - 1);\r\n",
                    "      } \r\n",
                    "\r\n",
                    "       return retmatches;\r\n",
                    "    }\r\n",
                    "\r\n",
                    "    function __doautocomplete(oevent, id)\r\n",
                    "    {\r\n",
                    "        var otextbox = document.getelementbyid(id + '_textbox');\r\n",
                    "        var frame = document.getelementbyid(id + '_autocompleteframe');\r\n",
                    "        var arrvalues =  document[id + '_data'];\r\n",
                    "       \r\n",
                    "        switch (oevent.keycode) \r\n",
                    "        {\r\n",
                    "            case 38: //up arrow  \r\n",
                    "            case 40: //down arrow\r\n",
                    "            case 37: //left arrow\r\n",
                    "            case 39: //right arrow\r\n",
                    "            case 33: //page up  \r\n",
                    "            case 34: //page down  \r\n",
                    "            case 36: //home  \r\n",
                    "            case 35: //end                  \r\n",
                    "            case 13: //enter  \r\n",
                    "            case 9: //tab  \r\n",
                    "            case 27: //esc  \r\n",
                    "            case 16: //shift  \r\n",
                    "            case 17: //ctrl  \r\n",
                    "            case 18: //alt  \r\n",
                    "            case 20: //caps lock\r\n",
                    "            case 8: //backspace  \r\n",
                    "            case 46: //delete\r\n",
                    "                return true;\r\n",
                    "                break;\r\n",
                    "                \r\n",
                    "            default:\r\n",
                    "                textboxreplaceselect(otextbox, string.fromcharcode(isie ? oevent.keycode : oevent.charcode));\r\n",
                    "                var ilen = otextbox.value.length;\r\n",
                    "\r\n",
                    "                var smatches = autocompletematch(otextbox.value, arrvalues);\r\n",
                    "\r\n",
                    "                if (smatches.length > 0)\r\n",
                    "                {\r\n",
                    "                   var arrmatches = smatches.split(',');\r\n",
                    "                   otextbox.value = arrmatches[0];\r\n",
                    "                   textboxselect(otextbox, ilen, otextbox.value.length);\r\n",
                    "                  \r\n",
                    "                   if (arrmatches.length > 1)\r\n",
                    "                   {\r\n",
                    "                        frame.style.display = 'inline';\r\n",
                    "                        frame.height = '100';\r\n",
                    "                        \r\n",
                    "                        frame.contentwindow.document.body.innerhtml = '';\r\n",
                    "                        for (var i = 0; i < arrmatches.length; i++)\r\n",
                    "                        {\r\n",
                    "                            frame.contentwindow.document.body.innerhtml += '<div style=\"width: 100%; cursor: default\" onmouseover=\"this.style.backgroundcolor=\\'#316ac5\\'; this.style.color=\\'white\\';\" onmouseout=\"this.style.backgroundcolor=\\'\\'; this.style.color=\\'black\\';\" onclick=\"parent.document.getelementbyid(\\'' + id + '_textbox\\').value = this.innerhtml\">' + arrmatches[i] + '</div>';\r\n",
                    "                        }\r\n",
                    "                        \r\n",
                    "                        frame.contentwindow.document.body.style.backgroundcolor = 'white';\r\n",
                    "                        frame.contentwindow.document.onclick = function() { document.getelementbyid(id + '_autocompleteframe').style.display = 'none'; };\r\n",
                    "                   }  \r\n",
                    "                }  \r\n",
                    "                \r\n",
                    "                return false;\r\n",
                    "        }        \r\n",
                    "    }\r\n",
                    "</script>\r\n",
                    "");
                page.clientscript.registerclientscriptblock(typeof(string), "__doautocomplete", script);
            }

            if (!page.clientscript.isclientscriptblockregistered(makeuniqueid("data")))
            {
                page.clientscript.registerclientscriptblock(typeof(string), makeuniqueid("data"), string.format("<script language=\"javascript\" type=\"text/javascript\">document.{0}_data = {1}</script>", makeuniqueid(null), tojsstringarray(autocompletedata)));
            }

            #endregion
        }

        protected override void rendercontents(htmltextwriter output)
        {
            output.writeline(string.format("<div onmouseleave=\"document.getelementbyid('{0}').style.display = 'none';\" style=\"width:{1}\" >", makeuniqueid("autocompleteframe"), width));
            textbox.rendercontrol(output);
            output.writeline("<br />");
            autocompleteframe.rendercontrol(output);
            output.writeline("</div>");
        }

        #endregion
    }
}

下载

http://teddyma.cnblogs.com/files/teddyma/autocompletetextbox.zip