Giving rich styles to auto-complete feature using StyledAutoCompleteExtender
Auto-complete is one of the key features in ajax style applications. As the most popular ajax toolkit for asp.net development, Ajax Control Toolkit provides AutoCompleteExtender control to build the feature without writing any JavaScript codes. The current version of AutoCompleteExtender is more powerful than before because that the user can input multiple words in the same text box by setting delimiter characters now. But it's still not so useful in some kinds of scenarios - in my opinion - since the control can only support plain text as the choices.
"How can we build the auto-complete feature like Google Suggest using AutoCompleteExtender?" The question like this has been asked times by the developers. The answser is "No, we can't" at the curent moment. But I've extended the AutoCompleteExtender to solve the problem.
The control I build which I called "StyledAutoCompleteExtender" extends the AutoCompleteExtender and the behavior class it uses on client-side extends the AutoCompleteBehavior. I keep most of the features the same as before to give the best compatibility. We can use the control just like the current AutoCompleteExtender and get the same result. But the new StyleAutoCompleteExtender control will accept a ItemTemplete value as the html template to display a single item. ItemTemplate gives us the flexibility to show the choices as the styles we want.
For example, we are building the auto-complete features like Google Suggest. What we should do first is to write the declarative code as following:
<asp:TextBox runat="server" ID="myTextBox" Width="400" style="margin:auto;"/>
<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"
CompletionListElementID="Panel1">
<ItemTemplate>
<span style="float:left;" class="keywords">{0}</span>
<span style="float:right;" class="result">{1} results</span>
<div style="clear:both;"></div>
</ItemTemplate>
</jeffz:StyledAutoCompleteExtender>
<asp:Panel ID="Panel1" runat="server"></asp:Panel>
Please focus on the ItemTemplate set in the AutoCompleteExtender. There're format parameters like "{0}" and "{1}" in it. It will be replaced by the value get from the method on server-side - I'll give more information of that later. Actuallly the value of ItemTemplate is the format string witch will be used as the paratemer of String.format() method so we can utilize the localization features in ASP.NET AJAX by setting format parameters as "{0:d}".
The values to replace the format item would be get from the web service method on server-side. Apparently, we'll probably get multiple values for a single chioces so the return type of the method should be modified. The following is web service method used for our "Google Suggest":
[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;
}
The new method should return a list contains an array of objects. Each array will be used to display a single item of choices. The objects in the array will be used as the rest paramesters when calling String.format() method. Please note that if the method returns an array of string as before, the ItemTemplate set in the control would be ignored and everything will work like the traditional AutoCompleteExtender. The final result is just like the following image (the css style defined in the page has been omitted).
The most important modification of StyleAutoCompleteExtender is the separation of "the text to complete user's typing" and "the content in the choices". In the current version of StyledAutoCompleteExtender, the first object in the array for each choise will be selected as "the text to complete user's typing". That means the first object could be ignored in ItemTemplate if necessary. The following sample is to simulate the email selection when using Windows Live Mail. Please note that the real feature is purely implemented on client-side but we're using StyleAutoCompleteExtender, which will get the choises from server-side.
Here're the defined css styles and the declarative code:
.completionListElement
{
visibility : hidden;
margin : 0px! important;
background-color : inherit;
color : black;
border : solid 1px gray;
cursor : pointer;
text-align : left;
list-style-type : none;
font-family : Verdana;
font-size: 11px;
padding : 0;
}
.listItem
{
background-color: white;
padding : 1px;
}
.highlightedListItem
{
background-color: #c3ebf9;
padding : 1px;
}
<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>
And here's the corresponding web service method. Please note that each item in the list is an array with 2 elements. The first one is the text would be set in the textbox and second one is the html string for a single choise.
private static string[] s_emails = new string[]
{
"\"jeffrey zhao at yahoo\" <jeffz@yahoo.com>",
"\"jeffrey zhao at yahoo china\" <jeffz@yahoo.com.cn>",
"\"jeffrey zhao at live mail\" <jeffz@live.com>",
"\"jeffrey zhao at gmail\" <jeffz@gmail.com>"
};
[WebMethod]
public IList<object[]> GetEmailCompletionList(
string prefixText, int count)
{
IList<object[]> items = new List<object[]>();
prefixText = prefixText.Trim();
foreach (string email in s_emails)
{
if (email.Contains(prefixText))
{
string encoded = email.Replace(
prefixText, "<b>" + prefixText + "</b>");
items.Add(new object[]
{
HttpUtility.HtmlDecode(email) + ";",
encoded
});
}
}
return items;
}
Since we have set the DelimiterCharacters property int the control we can type multiple words in the textbox. The final result is showing in the following image.
The implementation of StyleAutoCompleteExtender is not so complex as I thought before and I'll explain it in another post. The attachment is the code of the control with two samples. Please feel free to contract me if you found problems and any suggestions would be better. :)