30 October 2008
The Silverlight AutoCompleteBox is now available. This guide covers the control's properties, usage, and demonstrates how to hook it up to a JSON web service.
Note: This post contains live Silverlight content that your news reader may not be able to display.
The auto complete pattern is well-known, useful, time-tested. There's been one in IE for ages. You expect that your e-mail client auto completes. Auto complete functionality can be found in controls in Winforms, WPF, AJAX, Flex. And now, Silverlight.
With the Silverlight Toolkit, we've decided to go beyond text-only suggestions. With basics in mind, we designed a flexible & extensible control that lets modern app developers use data binding, styling, data templates, and more.
Auto complete features you expect
Silverlight AutoCompleteBox features
To get started, just set the ItemsSource property to an IEnumerable. You're then hooked up to the default filtering and are ready to go.
I'm going to walk you through some simple scenarios now using the control assemblies that we shipped in the toolkit. To follow along, you'll need Visual Studio 2008 (SP1) with the Silverlight 2 Tools installed, plus the free Silverlight Toolkit.
First, create a new Silverlight application project: open Visual Studio, go to the File | New | Project menu option, and create a new C# 'Silverlight Application' project.
In the 'Add Silverlight Application' dialog, select the 2nd option to 'Automatically generate a test page', and click Ok.
To add a reference to the main toolkit control library, right-click on the project in the Solution Explorer and select 'Add Reference'.
Now, click on the 'Browse' tab, locate the extracted toolkit folders, and move into the 'Binaries' directory. Select the 'Microsoft.Windows.Controls.dll' file and click Ok.
If you create a new Silverlight Application project and add a reference to the Microsoft.Windows.Controls.dll assembly that we shipped in the toolkit, you can follow along and learn about the control.
Inside Microsoft.Windows.Controls we included ObjectCollection. It makes it easy to create collections in SIlverlight XAML.
Inside Page.xaml:
Here's the Page.xaml file:
<UserControl x:Class="SilverlightApplication6.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:clr="clr-namespace:System;assembly=mscorlib" xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls" Width="400" Height="300"> <UserControl.Resources> <controls:ObjectCollection x:Key="Months"> <clr:String>January</clr:String> <clr:String>February</clr:String> <clr:String>March</clr:String> <clr:String>April</clr:String> <clr:String>May</clr:String> <clr:String>June</clr:String> <clr:String>July</clr:String> <clr:String>August</clr:String> <clr:String>September</clr:String> <clr:String>October</clr:String> <clr:String>November</clr:String> <clr:String>December</clr:String> </controls:ObjectCollection> </UserControl.Resources> <StackPanel VerticalAlignment="Top" Margin="5"> <controls:AutoCompleteBox ItemsSource="{StaticResource Months}" /> </StackPanel> </UserControl>
If you press F5 to run the project, you'll get a text box that lets you type in a month.
Here is the application:
Do know that in the current release, this control does not respect the INotifyCollectionChanged interface. If you're changing the underlying data, please reset the ItemsSource to a new list. We'll address this in a future release.
ItemsSource
Set your data here.
Text
Contains the current text that the user has entered or selected.
The TextChanged event is fired whenever the text is updated. Most classic auto complete applications function like text boxes, so those apps can use the Text and TextChanged events as they are.
You can also replace most simple TextBox instances in your applications with AutoCompleteBox.
IsTextCompletionEnabled
This property is true by default. It will perform a search on your items and look for the best match; if one is found, the text box is updated with the item's string value, and the rest of the text will be highlighted.
SelectedItem
Is null if no item is currently selected.
If the user navigates through the drop down, selecting items, or clicks on an item, this will be updated to that item.
The SelectedItemChanged event fires when the item changes.
If you enter text into the control, such as "Steve Ballmer," the SelectedItem may not be updated to an item unless the list box item is selected or if you have text completion enabled.
SearchMode
Selects the built-in search filter to use. The default is "StartsWith," and checks that each item's string value starts with the user's search string. "Contains" is another very useful value, as is "None".
If you connect AutoCompleteBox to a web server that performs its own filtering, you'll probably want to set the SearchMode to 'None'.
MinimumPopulateDelay
This is an amount of time that elapses after the user types and the population event is fired.
The default value of 0 is very quick, so every time the user enters new character, the Populating event is fired. If you're connected to a web service, you'll want to step back and place a reasonable value in here so that not every keypress goes straight to the server.
MinimumPrefixLength
This is the minimum number of characters that must be entered before the control looks for suggestions. The default value is 1, but a lot of classic AJAX auto complete controls use 3 as the default value, so this is a common customization property.
Advanced scenarios might make use of:
TextFilter
Provide a lambda/delegate that takes two parameters: the search string, and the string that represents an item. The function needs to return bool true or false, indicating whether the item should be shown as a suggestion in the drop down list.
ItemFilter
The more fun version of TextFilter, this takes a lambda expression that has two inputs: the search string, and the item as an object. You are then free to use your own filtering logic with your rich data types to return true or false.
Populating event
Called whenever the control is ready to search for suggestions, this is your opportunity to intercept the standard filtering and manage the items source first.
If you have the data available immediately, before your handler returns, you can simply change the ItemsSource property of the AutoCompleteBox control right here, no other changes necessary.
But if you're going to perform an asynchronous operation (background thread calculations; call a web service), you need to set the Cancel property of the event arguments to True. This will cancel the built-in AutoComplete filtering and effectively tell the control to hold off until you are ready.
To continue the suggestion process, you need to then call the PopulateComplete method. In your web service completion event, you'll want to place this call.
Populated event
This is called once suggestions are found and ready to be displayed. The Data property of the event arguments gives you read-only access to the view that will be provided to the selection control.
DropDownOpening, DropDownOpened, DropDownClosing, DropDownClosed
A nice set of drop down events. The Opening and Closing events can be canceled.
Setting the ItemsSource in XAML is not a real scenario. Enter web services, business object collections, XML files.
The ItemsSource property will take any IEnumerable. Linq fans rejoice. If you don't have the data ahead of time, you can subscribe to the Populating event of AutoCompleteBox and update the ItemsSource with the appropriate results, synchronously or asynchronously.
Using ItemsSource with a collection
If your application already has a set of data available (list of names, parsed XML, set of static city and state names), you can just set the items source in code to your enumerable collection.
Here's another quick app. The XAML is:
<UserControl x:Class="SilverlightApplication7.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls" Width="400" Height="100"> <StackPanel VerticalAlignment="Top" Margin="5"> <controls:AutoCompleteBox x:Name="AutoComplete1" /> </StackPanel> </UserControl>
And the code behind file:
using System.Windows; using System.Windows.Controls; namespace SilverlightApplication7 { public partial class Page : UserControl { public Page() { InitializeComponent(); Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { AutoComplete1.ItemsSource = new string[] { "Steve Ballmer", "Bill Gates", }; } } }
Connecting to a JSON web service
Most AJAX auto complete controls today use JSON. The transmit format is lightweight, able to transfer rich object data, and supported by just about everything out there. The System.Json assembly in Silverlight makes it easy to parse in VB and C#.
Here is a minimalistic example. I have this PHP page deployed:
<?php header("Content-type: application/json"); $var = "prefix"; if (isset($_GET[$var]) and $_GET[$var] != "") { // Shorten $prefix = substr($_GET[$var], 0, 40); // Strip $s = strip_tags($prefix); // Create suggestions $suggestions = array(); for ($i = 0; $i < 11; $i++) { $suggestions[] = $s . $i; } print json_encode($suggestions); } ?>
I'm using the same Page.xaml from before. To get ready to use JSON, I need to:
The code behind file doesn't need many changes to deal with a web service.
The basic idea is to intercept the Populating event, cancel it, and kick off a web client. In the completion event for the web client, we update the items source of the AutoCompleteBox and call the PopulateComplete method to display the suggestions.
In my app, I first subscribe to Population. I set some basic properties too:
private void OnLoaded(object sender, RoutedEventArgs e) { AutoComplete1.IsTextCompletionEnabled = false; // Server does the filtering AutoComplete1.SearchMode = AutoCompleteSearchMode.None; AutoComplete1.Populating += (s, args) => { args.Cancel = true; WebClient wc = new WebClient(); string prefix = HttpUtility.UrlEncode(args.Parameter); Uri service = new Uri("http://www.jwpc.com/services/suggest/?prefix=" + prefix); wc.DownloadStringCompleted += DownloadStringCompleted; wc.DownloadStringAsync(service, s); }; }
Then, in the DownloadStringCompleted handler, I parse the JSON response:
private void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { AutoCompleteBox acb = e.UserState as AutoCompleteBox; if (acb != null && e.Error == null && !e.Cancelled && !string.IsNullOrEmpty(e.Result)) { List<string> suggestions = new List<string>(); JsonArray ja = JsonArray.Parse(e.Result) as JsonArray; foreach (JsonPrimitive suggestion in ja) { suggestions.Add(suggestion.ToString()); } if (suggestions.Count > 0) { acb.ItemsSource = suggestions; acb.PopulateComplete(); } } }
That's all there is to it!
Real world AutoComplete example
Inside the Silverlight Toolkit samples project, I've included a similar example of connecting to a JSON web service. It uses the excellent Live search suggestions. In this sample, the MinimumPopulateDelay is set to 150 milliseconds.
Here's the Live.com sample:
For performance reasons, we haven't exposed the ability for you to creating a binding expression linking your items and the text that should be used in comparisons.
When you bind to a list of objects, the string representation is evaluated in the following order:
So, if you own the data classes in your application, you'll want to expose the most meaningful name (such as a person's full name, or employee ID), since the default ToString operator is going to return the type name of your item instead.
You can also provide a value converter in all situations.
You can use the ItemTemplate property to provide a DataTemplate that is used for rendering items.
These dependency properties can be used for styling:
In the Live search example, the text box style was:
<UserControl.Resources> <Style x:Key="SearchTextBoxStyle" TargetType="TextBox"> <Setter Property="Background" Value="#AAFFFFFF" /> <Setter Property="FontFamily" Value="Verdana" /> <Setter Property="FontSize" Value="18" /> <Setter Property="Padding" Value="2" /> </Style> </UserControl.Resources>
And the AutoCompleteBox XAML:
<controls:AutoCompleteBox SearchMode="None" x:Name="Search" TextBoxStyle="{StaticResource SearchTextBoxStyle}" IsTextCompletionEnabled="False" IsEnabled="False" />
The control defines four template parts, similar to ComboBox; however, the default control template only provides 3 of those parts, since it does not expose a toggle button. All 4 are:
I hope this gives you what you need to get started using AutoCompleteBox in your own applications. In the coming days I'll be posting some details and fun examples of how you can really make your apps shine with this control.
If you're looking for other posts about the control, I'd recommend checking out Jonas Follesø's AutoCompleteBox post too. He's a blogging machine!
Hope this helps.
See also:
AutoCompleteBox: The 5 things you need to know
AutoCompleteBox Video Introduction
Jeff Wilcox is a Software Engineer at Microsoft in the Open Source Programs Office (OSPO), helping Microsoft engineers use, contribute to and release open source at scale.