• 提供多单词建议的自定义AutoCompleteExtender


    默认情况下,AutoCompleteExtender显示的结果来自于文本框中输入的全部值,这里我的实现,它可以去搜索文本框中多于一个的单词,它们之间用逗号分割(或者别的符号),任何时间输入逗号,将会显示一个新的建议下拉列表。AutoCompleteExtender并不支持这种类型的列表,我们将通过一些修改来实现这些属性。[英文原文来自于CodeProject]

     

    主要内容

    1.简介

    2.继承AutoCompleteProperties

    3.继承AutoCompleteExtender

    4.实现自定义的AutoCompleteBehavior

    5.测试

     

    简介

    默认情况下,AutoCompleteExtender显示的结果来自于文本框中输入的全部值,这里我的实现,它可以去搜索文本框中多于一个的单词,它们之间用逗号分割(或者别的符号),任何时间输入逗号,将会显示一个新的建议下拉列表。AutoCompleteExtender并不支持这种类型的列表,我们将通过一些修改来实现这些属性。

     

    继承AutoCompleteProperties

    第一步为新控件CustomAutoCompleteExtender创建C#类,我们将定义一个继承于AutoCompleteProperties的类CustomAutoCompleteProperties,并添加多单词建议支持和CSS样式属性,为了实现多单词建议,我们需要一个属性SeparatorChar,通过该属性,我们可以拆分成一个一个的单词并为最新输入的单词打开建议列表。

    namespace CustomAtlas.Controls

    {

        
    public class CustomAutoCompleteProperties : AutoCompleteProperties

        
    {

            
    public string SeparatorChar

            
    {

                
    get

                
    {

                    
    object obj = base.ViewState["SeparatorChar"];

                    
    if (obj != nullreturn (string)obj;

                    
    else return ",";

                }


                
    set

                
    {

                    
    base.ViewState["SeparatorChar"= value;

                    
    base.OnChanged(EventArgs.Empty);

                }


            }


     

            
    public string CssList

            
    {

                
    get

                
    {

                    
    object obj = base.ViewState["CssList"];

                    
    if (obj != nullreturn (string)obj;

                    
    else return String.Empty;

                }


                
    set

                
    {

                    
    base.ViewState["CssList"= value;

                    
    base.OnChanged(EventArgs.Empty);

                }


            }


     

            
    public string CssItem

            
    {

                
    get

                
    {

                    
    object obj = base.ViewState["CssItem"];

                    
    if (obj != nullreturn (string)obj;

                    
    else return String.Empty;

                }


                
    set

                
    {

                    
    base.ViewState["CssItem"= value;

                    
    base.OnChanged(EventArgs.Empty);

                }


            }


     

            
    public string CssHoverItem

            
    {

                
    get

                
    {

                    
    object obj = base.ViewState["CssHoverItem"];

                    
    if (obj != nullreturn (string)obj;

                    
    else return String.Empty;

                }


                
    set

                
    {

                    
    base.ViewState["CssHoverItem"= value;

                    
    base.OnChanged(EventArgs.Empty);

                }


            }


        }


    }

    CssList, CssItemCssHoverItem需要构建控件的样式,CssList提供用来画下拉列表,CssItemCssHoverItem用来画列表中的每一项。

     

    继承AutoCompleteExtender

    完成了第一步,我们继续实现Extender,在这里我们需要从AutoCompleteExtender继承并为控件添加新的属性。

    namespace CustomAtlas.Controls

    {

        
    public class CustomAutoCompleteExtender : AutoCompleteExtender

        
    {

            
    protected override void RenderScript(Microsoft.Web.Script.ScriptTextWriter writer, Control targetControl)

            
    {

                
    // get our CustomAutoCompleteProperties

                CustomAutoCompleteProperties cacp 
    = (CustomAutoCompleteProperties) base.GetTargetProperties(targetControl);

     

                
    if ((cacp != null&& cacp.Enabled)

                
    {

                    
    // check if the ServicePath is set

                    
    string _ServicePath = cacp.ServicePath;

                    
    if (_ServicePath == String.Empty)

                    
    {

                        _ServicePath 
    = this.ServicePath;

                    }


                    
    if (_ServicePath == String.Empty)

                    
    {

                        
    throw new InvalidOperationException("The ServicePath must be set for AutoCompleteBehavior");

                    }


     

                    
    // check if the ServiceMethod is set

                    
    string _ServiceMethod = cacp.ServiceMethod;

                    
    if (_ServiceMethod == String.Empty)

                    
    {

                        _ServiceMethod 
    = this.ServiceMethod;

                    }


                    
    if (_ServiceMethod == String.Empty)

                    
    {

                        
    throw new InvalidOperationException("The ServiceMethod must be set for AutoCompleteBehavior");

                    }


     

                    
    // search for the completion list control if an ID was supplied

                    Control c 
    = null;

                    
    string drp = this.DropDownPanelID;

                    
    if (drp != String.Empty)

                    
    {

                        c 
    = this.NamingContainer.FindControl(drp);

                        
    if (c == null)

                        
    {

                            
    throw new InvalidOperationException("The specified DropDownPanelID is not a valid ID");

                        }


                    }


     

                    
    // write the Atlas markup on page

                    writer.WriteStartElement(
    "autoComplete");

                    writer.WriteAttributeString(
    "serviceURL"base.ResolveClientUrl(_ServicePath));

                    writer.WriteAttributeString(
    "serviceMethod", _ServiceMethod);

                    
    if (c != null) writer.WriteAttributeString("completionList", c.ClientID);

                    writer.WriteAttributeString(
    "minimumPrefixLength", cacp.MinimumPrefixLength.ToString());

                    writer.WriteAttributeString(
    "separatorChar", cacp.SeparatorChar);

                    writer.WriteAttributeString(
    "cssList", cacp.CssList);

                    writer.WriteAttributeString(
    "cssItem", cacp.CssItem);

                    writer.WriteAttributeString(
    "cssHoverItem", cacp.CssHoverItem);

                    writer.WriteEndElement();

                }


            }


        }


    }

     

    实现自定义的AutoCompleteBehavior

    现在控件已经完成,我们只剩下管理客户端代码以发送正确的值到WebService并提供我们自定义的CSS样式。在Atlas.js中查找到AutoCompleteBehavior,我们可以拷贝到这里并注册我们自己的类。

    ype.registerNamespace('Custom.UI');

     

    Custom.UI.AutoCompleteBehavior 
    = function() {

        Custom.UI.AutoCompleteBehavior.initializeBase(
    this);

        

        
    var _appURL;

        
    var _serviceURL;

        
    var _serviceMethod;

        
    var _separatorChar = ',';

        
    var _minimumPrefixLength = 3;

        
    var _cssList;

        
    var _cssItem;

        
    var _cssHoverItem;

        
    var _completionSetCount = 10;

        
    var _completionInterval = 1000;

        
    var _completionListElement;

        
    var _popupBehavior;

        

        
    var _timer;

        
    var _cache;

        
    var _currentPrefix;

        
    var _selectIndex;

        

        
    var _focusHandler;

        
    var _blurHandler;

        
    var _keyDownHandler;

        
    var _mouseDownHandler;

        
    var _mouseUpHandler;

        
    var _mouseOverHandler;

        
    var _tickHandler;

        

        
    this.get_appURL = function() {

            
    return _appURL;

        }


        
    this.set_appURL = function(value) {

            _appURL 
    = value;

        }


     

        
    this.get_completionInterval = function() {

            
    return _completionInterval;

        }


        
    this.set_completionInterval = function(value) {

            _completionInterval 
    = value;

        }


        

        
    this.get_completionList = function() {

            
    return _completionListElement;

        }


        
    this.set_completionList = function(value) {

            _completionListElement 
    = value;

        }


        

        
    this.get_completionSetCount = function() {

            
    return _completionSetCount;

        }


        
    this.set_completionSetCount = function(value) {

            _completionSetCount 
    = value;

        }


        

        
    this.get_minimumPrefixLength = function() {

            
    return _minimumPrefixLength;

        }


        
    this.set_minimumPrefixLength = function(value) {

            _minimumPrefixLength 
    = value;

        }


        

        
    this.get_separatorChar = function() {

            
    return _separatorChar;

        }


        
    this.set_separatorChar = function(value) {

            _separatorChar 
    = value;

        }


        

        
    this.get_serviceMethod = function() {

            
    return _serviceMethod;

        }


        
    this.set_serviceMethod = function(value) {

            _serviceMethod 
    = value;

        }


        

        
    this.get_serviceURL = function() {

            
    return _serviceURL;

        }


        
    this.set_serviceURL = function(value) {

            _serviceURL 
    = value;

        }


        

        
    /* styles */

        
    this.get_cssList = function() {

            
    return _cssList;

        }


        
    this.set_cssList = function(value) {

            _cssList 
    = value;

        }


        
    this.get_cssItem = function() {

            
    return _cssItem;

        }


        
    this.set_cssItem = function(value) {

            _cssItem 
    = value;

        }


        
    this.get_cssHoverItem = function() {

            
    return _cssHoverItem;

        }


        
    this.set_cssHoverItem = function(value) {

            _cssHoverItem 
    = value;

        }


     

        
    this.dispose = function() {

            
    if (_timer) {

                _timer.tick.remove(_tickHandler);

                _timer.dispose();

            }


            

            
    var element = this.control.element;

            element.detachEvent('onfocus', _focusHandler);

            element.detachEvent('onblur', _blurHandler);

            element.detachEvent('onkeydown', _keyDownHandler);

            

            _completionListElement.detachEvent('onmousedown', _mouseDownHandler);

            _completionListElement.detachEvent('onmouseup', _mouseUpHandler);

            _completionListElement.detachEvent('onmouseover', _mouseOverHandler);

            

            _tickHandler 
    = null;

            _focusHandler 
    = null;

            _blurHandler 
    = null;

            _keyDownHandler 
    = null;

            _mouseDownHandler 
    = null;

            _mouseUpHandler 
    = null;

            _mouseOverHandler 
    = null;

     

            Sys.UI.AutoCompleteBehavior.callBaseMethod(
    this, 'dispose');

        }


     

        
    this.getDescriptor = function() {

            
    var td = Custom.UI.AutoCompleteBehavior.callBaseMethod(this, 'getDescriptor');

            td.addProperty('completionInterval', Number);

            td.addProperty('completionList', Object, 
    false, Sys.Attributes.Element, true);

            td.addProperty('completionSetCount', Number);

            td.addProperty('minimumPrefixLength', Number);

            td.addProperty('separatorChar', String);

            td.addProperty('cssList', String);

            td.addProperty('cssItem', String);

            td.addProperty('cssHoverItem', String);

            td.addProperty('serviceMethod', String);

            td.addProperty('serviceURL', String);

            td.addProperty('appURL', String);

            
    return td;

        }


        

        
    this.initialize = function() {

            Custom.UI.AutoCompleteBehavior.callBaseMethod(
    this, 'initialize');

     

            _tickHandler 
    = Function.createDelegate(thisthis._onTimerTick);

            _focusHandler 
    = Function.createDelegate(thisthis._onGotFocus);

            _blurHandler 
    = Function.createDelegate(thisthis._onLostFocus);

            _keyDownHandler 
    = Function.createDelegate(thisthis._onKeyDown);

            _mouseDownHandler 
    = Function.createDelegate(thisthis._onListMouseDown);

            _mouseUpHandler 
    = Function.createDelegate(thisthis._onListMouseUp);

            _mouseOverHandler 
    = Function.createDelegate(thisthis._onListMouseOver);

            

            _timer 
    = new Sys.Timer();

            _timer.set_interval(_completionInterval);

            _timer.tick.add(_tickHandler);

            

            
    var element = this.control.element;

            element.autocomplete 
    = "off";

            element.attachEvent('onfocus', _focusHandler);

            element.attachEvent('onblur', _blurHandler);

            element.attachEvent('onkeydown', _keyDownHandler);

            

            
    var elementBounds = Sys.UI.Control.getBounds(element);

            

            
    if (!_completionListElement) {

                _completionListElement 
    = document.createElement('DIV');

                document.body.appendChild(_completionListElement);

            }


            

            
    // apply styles

            
    var completionListStyle = _completionListElement.style;

            
    if ( _cssList != '' ) 

            
    {

                _completionListElement.className 
    = _cssList;

            }
     

            
    else 

            
    {

                completionListStyle.backgroundColor 
    = 'window';

                completionListStyle.color 
    = 'windowtext';

                completionListStyle.border 
    = 'solid 1px buttonshadow';

                completionListStyle.cursor 
    = 'default';

            }


            
    // default styles

            completionListStyle.unselectable 
    = 'unselectable';

            completionListStyle.overflow 
    = 'hidden';

            completionListStyle.visibility 
    = 'hidden';

            completionListStyle.width 
    = (elementBounds.width - 2+ 'px';

            

            _completionListElement.attachEvent('onmousedown', _mouseDownHandler);

            _completionListElement.attachEvent('onmouseup', _mouseUpHandler);

            _completionListElement.attachEvent('onmouseover', _mouseOverHandler);

            document.body.appendChild(_completionListElement);

            
    var popupControl = new Sys.UI.Control(_completionListElement);

            _popupBehavior 
    = new Sys.UI.PopupBehavior();

            _popupBehavior.set_parentElement(element);

            _popupBehavior.set_positioningMode(Sys.UI.PositioningMode.BottomLeft);

            popupControl.get_behaviors().add(_popupBehavior);

            _popupBehavior.initialize();

            popupControl.initialize();

        }


        

        
    this._hideCompletionList = function() {

            _popupBehavior.hide();

            _completionListElement.innerHTML 
    = '';

            _selectIndex 
    = -1;

        }


        

        
    this._highlightItem = function(item) {

            
    var children = _completionListElement.childNodes;

            
    // non-selecteditems

            
    for (var i = 0; i < children.length; i++{

                
    var child = children[i];

                
    if (child != item) {

                    
    if ( _cssItem != '' ) 

                    
    {

                        child.className 
    = _cssItem;

                    }


                    
    else

                    
    {

                        child.style.backgroundColor 
    = 'window';

                        child.style.color 
    = 'windowtext';

                    }


                }


            }


            
    // selected item

            
    if ( _cssHoverItem != '' ) 

            
    {

                item.className 
    = _cssHoverItem;

            }


            
    else 

            
    {

                item.style.backgroundColor 
    = 'highlight';

                item.style.color 
    = 'highlighttext';

            }


        }


        

        
    this._onListMouseDown = function() {

            
    if (window.event.srcElement != _completionListElement) {

                
    this._setText(window.event.srcElement.firstChild.nodeValue);

            }


        }


        

        
    this._onListMouseUp = function() {

            
    this.control.focus();

        }


        

        
    this._onListMouseOver = function() {

            
    var item = window.event.srcElement;

            _selectIndex 
    = -1;

            
    this._highlightItem(item);

        }


     

        
    this._onGotFocus = function() {

            _timer.set_enabled(
    true);

        }


        

        
    this._onKeyDown = function() {

            
    var e = window.event;

            
    if (e.keyCode == 27{

                
    this._hideCompletionList();

                e.returnValue 
    = false;

            }


            
    else if (e.keyCode == Sys.UI.Key.Up) {

                
    if (_selectIndex > 0{

                    _selectIndex
    --;

                    
    this._highlightItem(_completionListElement.childNodes[_selectIndex]);

                    e.returnValue 
    = false;

                }


            }


            
    else if (e.keyCode == Sys.UI.Key.Down) {

                
    if (_selectIndex < (_completionListElement.childNodes.length - 1)) {

                    _selectIndex
    ++;

                    
    this._highlightItem(_completionListElement.childNodes[_selectIndex]);

                    e.returnValue 
    = false;

                }


            }


            
    else if (e.keyCode == Sys.UI.Key.Return) {

                
    if (_selectIndex != -1{

                    
    this._setText(_completionListElement.childNodes[_selectIndex].firstChild.nodeValue);

                    e.returnValue 
    = false;

                }


            }


            

            
    if (e.keyCode != Sys.UI.Key.Tab) {

                _timer.set_enabled(
    true);

            }


        }


        

        
    this._onLostFocus = function() {

            _timer.set_enabled(
    false);

            
    this._hideCompletionList();

        }


        

        
    function _onMethodComplete(result, response, context) {

            
    var acBehavior = context[0];

            
    var prefixText = context[1];

            acBehavior._update(prefixText, result,  
    true);

        }


        

        
    this._onTimerTick = function(sender, eventArgs) {

            
    if (_serviceURL && _serviceMethod) {

            

                
    var text = this.control.element.value;

                

                
    if ( text.lastIndexOf(_separatorChar) > -1 ) 

                
    {

                    
    // found separator char in the text

                    
    var pos = text.lastIndexOf(_separatorChar);

                    pos
    ++;

                    text 
    = text.substring(pos, (text.length));

                    text 
    = text.trim();

                }


                

                
    if (text.trim().length < _minimumPrefixLength) {

                    
    this._update('', null,  false);

                    
    return;

                }


                

                
    if (_currentPrefix != text) {

                    _currentPrefix 
    = text;

                    
    if (_cache && _cache[text]) {

                        
    this._update(text, _cache[text],  false);

                        
    return;

                    }


                    

                    Sys.Net.ServiceMethod.invoke(_serviceURL, _serviceMethod, _appURL,

                                                              
    { prefixText : _currentPrefix, count: _completionSetCount },

                                                              _onMethodComplete, 
    nullnullnull,

                                                              [ 
    this, text ]);

                }


            }


        }


        

        
    this._setText = function(text) {

            _timer.set_enabled(
    false);

            _currentPrefix 
    = text;

            
    if (Sys.UI.TextBox.isInstanceOfType(this.control)) {

                
    this.control.set_text(text);

            }


            
    else {

                
    var currentValue = this.control.element.value;

                
    if ( currentValue.lastIndexOf(_separatorChar) > -1 ) 

                
    {

                    
    // found separator char in the text

                    
    var pos = currentValue.lastIndexOf(_separatorChar);

                    pos
    ++;

                    currentValue 
    = currentValue.substring(0, pos) + text;

                }
     

                
    else 

                
    {

                    
    // no separator char found

                    currentValue 
    = text;

                }


                
    this.control.element.value = currentValue;

            }


            
    this._hideCompletionList();

        }


        

        
    this._update = function(prefixText, completionItems, cacheResults) {

            
    if (cacheResults) {

                
    if (!_cache) {

                    _cache 
    = { };

                }


                _cache[prefixText] 
    = completionItems;

            }


     

            _completionListElement.innerHTML 
    = '';

            _selectIndex 
    = -1;

            
    if (completionItems && completionItems.length) {

                
    for (var i = 0; i < completionItems.length; i++{

                    
    var itemElement = document.createElement('div');

                    itemElement.appendChild(document.createTextNode(completionItems[i]));

                    itemElement.__item 
    = '';

                    
    if ( _cssItem != '' ) 

                    
    {

                        itemElement.className 
    = _cssItem;

                    }


                    
    else

                    
    {                

                        
    var itemElementStyle = itemElement.style;

                        itemElementStyle.padding 
    = '1px';

                        itemElementStyle.textAlign 
    = 'left';

                        itemElementStyle.textOverflow 
    = 'ellipsis';

                        itemElementStyle.backgroundColor 
    = 'window';

                        itemElementStyle.color 
    = 'windowtext';

                    }
                    

                    _completionListElement.appendChild(itemElement);

                }


                _popupBehavior.show();

            }


            
    else {

                _popupBehavior.hide();

            }


        }


    }


    Custom.UI.AutoCompleteBehavior.registerSealedClass('Custom.UI.AutoCompleteBehavior', Sys.UI.Behavior);

    Sys.TypeDescriptor.addType('script', 'autoComplete', Custom.UI.AutoCompleteBehavior);

    首先我们在Extender类中添加了四个已经创建的属性,现在我们可以在.aspx页面中使用这些属性的值。

    在代码中找到函数_onTimerTick,它用来延迟显示下拉列表,在该函数中,我们可以发送该值到WebService,如果需要我们也可以改变它的值。

    var text = this.control.element.value;

    if ( text.lastIndexOf(_separatorChar) > -1 ) 

    {

     
    // found separator char in the text, choosing the right word

     
    var pos = text.lastIndexOf(_separatorChar);

     pos
    ++;

     text 
    = text.substring(pos, (text.length));

    text 
    = text.trim();

    }

    现在,无论用户何时在文本框中输入一个值,AutoCompleteBehavior会验证驻留的分割字符,找到最后一个单词或者TextBox中的全部文本发送到WebService

     

    测试

    解决方案:AutoCompleteBehavior.js保存在scriptLibrary文件夹下,CustomAutoCompleteProperties.cs CustomAutoCompleteExtender.cs保存在App_Code文件夹下。

    创建一个新的.aspx文件,并且添加对CustomAutoCompleteExtender类的引用,并在页面中放一些控件:

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

    <%@ Register Namespace="CustomAtlas.Controls" TagPrefix="customAtlas" %>

     

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">

    <head runat="server">

        
    <title>CustomAutoCompleteExtender</title>

        
    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />

    </head>

    <body>

        
    <form id="form1" runat="server">

            
    <atlas:ScriptManager ID="scriptManager" runat="server">

                
    <Scripts>

                    
    <atlas:ScriptReference ScriptName="Custom" />

                    
    <atlas:ScriptReference Path="scriptLibrary/CustomAutoCompleteBehavior.js" />

                
    </Scripts>

            
    </atlas:ScriptManager>

            

            
    <div>

                
    <asp:TextBox ID="txtSuggestions" runat="server"></asp:TextBox>

                
    <customAtlas:CustomAutoCompleteExtender ID="CustomAutoCompleteExtender1" runat="server">

                    
    <customAtlas:CustomAutoCompleteProperties

                                     
    TargetControlID="txtSuggestions"

                                     ServicePath
    ="WebServiceDemo.asmx"

                                     ServiceMethod
    ="GetSuggestions"

                                     MinimumPrefixLength
    ="1"

                                     SeparatorChar
    =","

                                     CssList
    ="autoCompleteList"

                                     CssItem
    ="autoCompleteItem" 

                                     CssHoverItem
    ="autoCompleteHoverItem"

                                     Enabled
    ="true" />

                
    </customAtlas:CustomAutoCompleteExtender>       

            
    </div>

        
    </form>

    </body>

    </html>

    调用一个简单的示例WebService,输入一个单词,再输入逗号(逗号是默认值),并重新输入一个新的单词,将会显示出一个新的建议下拉列表。显示效果如下:

    希望对你有所帮助!

    原文地址:http://www.codeproject.com/Ajax/CustomAutoCompleteExt.asp

  • 相关阅读:
    自定义提示框(alert、confirm 可自定义标题 内容 图标 取消按钮)
    Node.js 技術
    ASP.NET MVC+Spring.net+Nhibernate+EasyUI+Jquery开发案例(1)
    Memcached 命令简介
    MySQL 请选择合适的列
    WCF光芒下的Web Service
    如何实现共享软件网络授权认证,包括注册新用户、登录、修改密码等操作
    SOA面向服务架构简述
    Python Web 框架, 使用 Django 和 Python 开发 Web 站点
    Interface继承至System.Object?
  • 原文地址:https://www.cnblogs.com/Terrylee/p/Custom_AutoCompleteExtender_with_multiple_word_suggestions.html
Copyright © 2020-2023  润新知