代码改变世界

StyledAutoComplete - 支持丰富样式的自动补全

2007-06-12 18:15  Jeffrey Zhao  阅读(7592)  评论(64编辑  收藏  举报

几个月前,我扩展了Atlas CTP时期的的AutoCompleteBehavior,到了ASP.NET AJAX正式版之后这个扩展自然就不能用了。其实这段时间内有不少朋友问我该如何做到像Google Suggest那样带丰富样式的自动补全功能,但是由于各种原因(比如时间不够,还有现在的AutoCompleteBehavior复杂的许多,或者仅仅是“懒”……),我没有深入地研究它,更别提对它的扩展了。

最近手头正好有些时间,也就逼迫自己耐着性子读了读AutoCompleteBehavior的代码。不得不承认Ajax Control Tookit本身也在不停的发展之中,现在无论服务器端还是客户端都提供了非常强大的基础组件,解决了大量常见问题,使得开发工作变得简单了许多。与此形成鲜明对比的是文档的缺乏,建议对于深入ASP.NET AJAX感兴趣的朋友读一下Ajax Control Toolkit的代码,这对于您理解ASP.NET的控件模型与客户端组件的开发大有碑益。

我扩展了现有的AutoCompleteBehavior,使AutoComplete的功能能够轻松支持丰富的样式。我在这里暂时不对实现方法进行分析,不过大家可以下载代码并进行一些测试,如果有任何问题还可以反馈给我。另外,我认为目前StyledAutoCompleteBehavior的设计还不够灵活,如果有什么需要但是无法实现的功能请您告诉我,也欢迎大家给我一些功能设计方面的建议。自动补全是一个很常用的功能,可惜Ajax Control Toolkit中提供的功能实在不够用。

在我的扩展中,StyledAutoCompleteExtender继承了AutoCompleteExtender,StyledAutoCompleteBehavior继承了AutoCompleteBehavior,在编写时我也尽可能的保持了原有的功能不变,因此大家在使用时完全可以将AutoCompleteExtender的标签直接改为StyledAutoComplteExtender而不会影响到任何功能。与AutoCompleteExtender相比,StyledAutoCompleteExtender多了一个属性ItemTemplate用于指定自动补全中每一项的模板。例如我们要实现Google Suggest那样的功能,则需要这样使用StyledAutoCompleteExtender:

<jeffz:StyledAutoCompleteExtender
    runat="server" 
    BehaviorID="AutoCompleteEx"
    ID="autoComplete1" 
    TargetControlID="myTextBox"
    ServicePath="AutoComplete.asmx" 
    ServiceMethod="GetSearchCompletionList"
    MinimumPrefixLength="2" 
    CompletionInterval="1000"
    EnableCaching="true"
    CompletionSetCount="10"
    CompletionListCssClass="completionListElement" 
    CompletionListItemCssClass="listItem" 
    CompletionListHighlightedItemCssClass="highlightedListItem">
    <ItemTemplate>
        <span style="float:left;" class="keywords">{0}</span>
        <span style="float:right;" class="result">{1}&nbsp;results</span>
        <div style="clear:both;"></div>
    </ItemTemplate>
</jeffz:StyledAutoCompleteExtender>

请注意ItemTemplate里的内容,它将作为自动补全每一项的HTML。而诸如{0}、{1}等则作为占位符,最终在显示时将会使用服务器端返回的内容进行替换。为了配合这个功能,服务器端的Web Service方法返回值也有所变化:

[WebMethod]
public IList<object[]> GetSearchCompletionList(string prefixText, int count)
{
    if (count == 0)
    {
        count = 10;
    }

    Random random = new Random();
    IList<object[]> items = new List<object[]>(count);
    for (int i = 0; i < count; i++)
    {
        char c1 = (char)random.Next(65, 90);
        char c2 = (char)random.Next(97, 122);
        char c3 = (char)random.Next(97, 122);

        items.Add(new object[] { prefixText + c1 + c2 + c3, random.Next(10000, 300000) });
    }

    return items;
}

方法的返回值变成了一个存放object数组的列表,每个object数组将会作为自动补全中每一项的信息发送到客户端。object数组的每个元素将用于替换模板中的占位符(事实上,我使用了String.format方法来获得每一项的HTML,因此事实上您也可以在这里使用本地化的功能等等)。

模拟Google Suggest的效果如下(点击这里查看示例):

 

新的控件将“补全的内容”和“显示的内容”进行了分离。目前的控件将object数组的第一个元素(下标为0)作为“补全的内容”,在显示的时候,我们可以将其忽略。例如,下面的代码将会模拟Windows Live Mail中Email提示的功能(事实上,Live Mail的这部分自动补全是纯客户端功能,我这里模拟的仅仅是“样式”)。

<head id="Head1" runat="server">
    <title>Untitled Page</title>
    <style type="text/css">
        .completionListElement 
        {  
            visibility : hidden;
            margin : 0px!important;
            background-color : inherit;
            color : windowtext;
            border : buttonshadow;
            border-width : 1px;
            border-style : solid;
            cursor : pointer;
            overflow : auto;
            text-align : left; 
            list-style-type : none;
            font-family : Verdana;
            font-size: 11px;
            padding : 0;
        }
        .listItem 
        {
            background-color: white;
            padding : 1px;
        }        
        .highlightedListItem
        {
            background-color: #e9f5f7;
            padding : 1px;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
    
        <asp:TextBox runat="server" ID="myTextBox" Width="500"
            style="height:14px; border: solid 1px gray; font-family:Verdana; font-size: 11px;" />
        
        <jeffz:StyledAutoCompleteExtender
            runat="server" 
            BehaviorID="AutoCompleteEx"
            ID="autoComplete1" 
            TargetControlID="myTextBox"
            ServicePath="AutoComplete.asmx" 
            ServiceMethod="GetEmailCompletionList"
            MinimumPrefixLength="2" 
            CompletionInterval="1000"
            EnableCaching="true"
            CompletionSetCount="10"
            CompletionListCssClass="completionListElement" 
            CompletionListItemCssClass="listItem" 
            CompletionListHighlightedItemCssClass="highlightedListItem"
            DelimiterCharacters=",;">
            <ItemTemplate>{1}</ItemTemplate>
        </jeffz:StyledAutoCompleteExtender>
    </form>
</body>

在这里,ItemTemplate的内容很简单,只是将服务器端返回的每个object数组的第二个元素显示在页面上,而第一个元素则作为补全的文本。它对应的Web Service方法如下所示:

private static string[] emails = new string[]
{
    "\"jeffrey zhao at yahoo\" &lt;jeffz@yahoo.com&gt;",
    "\"jeffrey zhao at yahoo china\" &lt;jeffz@yahoo.com.cn&gt;",
    "\"jeffrey zhao at live mail\" &lt;jeffz@live.com&gt;",
    "\"jeffrey zhao at gmail\" &lt;jeffz@gmail.com&gt;"
};


[WebMethod]
public IList<object[]> GetEmailCompletionList(string prefixText, int count)
{
    IList<object[]> items = new List<object[]>();

    prefixText = prefixText.Trim();
    foreach (string email in emails)
    {
        if (email.Contains(prefixText))
        {
            string encoded = email.Replace(prefixText, "<b>" + prefixText + "</b>");
            items.Add(new object[] { HttpUtility.HtmlDecode(email) + ";", encoded });
        }
    }
        
    return items;
}

服务器端传回的每个object数组长度为2,第一个元素用于自动补全,第二个元素用于显示,因此需要将关键字部分进行加粗。使用效果如下(点击这里查看示例): 

 

   另外,正像Live Mail的功能一样,StyleAutoCompleteBehavior也支持输入多个Email——这是新版AutoCompleteBehavior提供的功能,请注意AutoCompleteBehavior的DelimiterCharacters属性,它是一个字符串,字符串中的每个字符即为分隔符。

目前StyledAutoCompleteBehavior只在FireFox和IE下进行了测试。

 

点击这里查看Google Suggest模拟示例

点击这里查看Windows Live Mail模拟示例

点击这里下载StyleAutoCompleteExtender/Behavior控件以及示例代码。