默认情况下,AutoCompleteExtender显示的结果来自于文本框中输入的全部值,这里我的实现,它可以去搜索文本框中多于一个的单词,它们之间用逗号分割(或者别的符号),任何时间输入逗号,将会显示一个新的建议下拉列表。AutoCompleteExtender并不支持这种类型的列表,我们将通过一些修改来实现这些属性。[英文原文来自于CodeProject]
主要内容
1.简介
2.继承AutoCompleteProperties
3.继承AutoCompleteExtender
4.实现自定义的AutoCompleteBehavior
5.测试
默认情况下,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 != null) return (string)obj;

else return ",";

}

set


{

base.ViewState["SeparatorChar"] = value;

base.OnChanged(EventArgs.Empty);

}

}


public string CssList


{

get


{

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

if (obj != null) return (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 != null) return (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 != null) return (string)obj;

else return String.Empty;

}

set


{

base.ViewState["CssHoverItem"] = value;

base.OnChanged(EventArgs.Empty);

}

}

}

}
CssList, CssItem和CssHoverItem需要构建控件的样式,CssList提供用来画下拉列表,CssItem和CssHoverItem用来画列表中的每一项。
继承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(this, this._onTimerTick);

_focusHandler = Function.createDelegate(this, this._onGotFocus);

_blurHandler = Function.createDelegate(this, this._onLostFocus);

_keyDownHandler = Function.createDelegate(this, this._onKeyDown);

_mouseDownHandler = Function.createDelegate(this, this._onListMouseDown);

_mouseUpHandler = Function.createDelegate(this, this._onListMouseUp);

_mouseOverHandler = Function.createDelegate(this, this._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, null, null, null,

[ 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)