(X) Hide this
    • Login
    • Join
      • Say No Bots Generate New Image
        By clicking 'Register' you accept the terms of use .

Using the AutoCompleteBox

(19 votes)
Peter Kuhn
>
Peter Kuhn
Joined Jan 05, 2011
Articles:   44
Comments:   29
More Articles
20 comments   /   posted on Jan 31, 2011
Categories:   Controls
This article is also available in print (Word, PDF) and e-reader formats (MOBI, EPUB).
Download all formats, including source code for $0.99.  Add to Cart

This article is compatible with the latest version of Silverlight.

The auto-complete box is a mix of a normal text box that allows the user to type in arbitrary text, and the drop-down list feature of a combo box that provides a fixed set of entries the user can select from. You're most likely using a control like this every day; for example, search engines like Bing and other web sites use these controls to provide a list of suggestions you can pick from without having to finish typing the full term, or they even provide a list of terms you entered previously to make repeated entries more comfortable for you.

The fact that the list of entries is filtered on the fly as the user types into the text box makes picking an entry from a large list possible very quickly. Even if users only remember parts of an entry name they can reduce the number of potential candidates dramatically by typing just a few characters. This is significantly different from other controls that only scroll to entries that start with what you type in, for example. In this article we'll see how the auto-complete box works and what options it offers; and although most people associate the control with string values only, we'll see that it's capable of handling far more than that and that it can work with any kind of data with little effort.

As always, you can download the complete source code at the end of the article. It contains all of the following samples as individual projects.

Basic usage

For the simplest scenario, you only need to provide the auto-complete box with an items source and process changes of the SelectedItem property either through data binding or by handling the SelectionChanged event. The source items do not necessarily need to be strings like in the following example. The control will automatically call the ToString() method on the items to create the text entries for the drop-down list, so if your data items override this method and a string representation is sufficient, you don't need to do anything else to get a working result. Alternatively, you can also hint the auto-complete box at what property shall be used for the string representation, as we'll see in a moment.

 <StackPanel x:Name="LayoutRoot" Background="White" Width="100">
     <TextBlock Text="{Binding ElementName=MyAutoCompleteBox, 
         Path=SelectedItem, 
         TargetNullValue='No item selected', 
         StringFormat='Selected Item: {0}'}"/>        
     <sdk:AutoCompleteBox x:Name="MyAutoCompleteBox"
                             ItemsSource="{Binding Items}" />
 </StackPanel>
 Items = new List<string>();
 Items.Add("One");
 Items.Add("Two");
 Items.Add("Three");
 Items.Add("Four");
 Items.Add("Five");
 Items.Add("Six");
 Items.Add("Seven");
 Items.Add("Eight");
 Items.Add("Nine");
 Items.Add("Ten");

This sample code will result in the following:

image

Surprisingly many aspects of the behavior of this control can be changed by built-in properties, with one of the most important ones being the way the auto-complete box filters the entries.

Changing the filter mode

By default, the auto-complete box filters entries by a "starts with" pattern. This means that if the string representation of an item starts with what is typed into the text box, it will be listed in the drop-down area. You can change this behavior with the FilterMode property of the control, which can be one of the predefined values of the AutoCompleteFilterMode enumeration. By changing this property, you can change the match algorithm to a "contains" or "equals" pattern and also use case-sensitive filtering instead of the default case-insensitive behavior.

Automatic text completion

One interesting property is the IsTextCompletionEnabled parameter. If you set this to true, the first possible match in the drop-down list is automatically selected and displayed in the textbox. To preserve fluent typing capabilities, the part of the match that wasn't typed in by the user is selected, so they can overwrite it if they just keep typing.

image

One drawback of this is that this actually changes the SelectedItem property whenever a different match from the drop-down list is selected and put into the text box. This is potentially undesired, because it might trigger additional (expensive or otherwise time-consuming) logic in your (view) model. Maybe that is the reason why the automatic completion is turned off by default.

Other behavioral properties

If you have a very large list of source items looking up potential matches may be expensive, or might result in a similar large list which doesn't really help the user much. In these cases, you can restrict the number of minimum characters the user has to type in until the drop-down list is populated with possible matches. If you set the corresponding property MinimumPrefixLength to a value of 3 for example, no list will be shown if the user has typed in less than three characters. The default value is 0.

A similar property is MinimumPopulateDelay (again the default value is 0) which allows to set the number of milliseconds that must expire after the user has typed in some text to kick of the population of the drop-down list. This is particularly useful in scenarios where you fetch the data for the drop-down list from a web service or similar (see below) and you need to minimize the number of network round-trips.

Styling of the AutoCompleteBox

Of course you can change the control template of the auto-complete box to change major parts of it. But in addition, the control exposes three properties that you can use directly to change the visual appearance of some of its inner parts more easily. Those three properties are: TextBoxStyle, ItemContainerStyle and ItemTemplate. They allow you to provide individual styles for the text box and the list box items. More advanced properties of the contained list box (like the items panel for example) are not exposed directly, but these three properties give you a lot of flexibility already. I won't go into the details of styling the control, as it's the same as styling stand-alone text boxes and list boxes, and also the linked MSDN documentation for the text box and item container style properties contain a full example for that. We'll use the item template to customize the control in the following parts though.

Advanced note: Although the auto-complete box very much behaves like an items control and offers some of the same properties like items source and item template, it actually does not inherit from the items control base class like the combo box does, for example. The reason for this is that the auto-complete box does not have a strong relation to a particular control for the drop-down part. The only requirement is that this selector implements the ISelectionAdapter interface. This means that you can change the default list box used for this to any control, even custom controls you've developed yourself. An auto-complete box using a tree view or similar is definitely a possible scenario. This gives you even more flexibility when you need to customize the control. However, this is an advanced topic that requires deriving from the auto-complete box and creating the actual selection adapter which I won't cover in this article.

Using custom data and an item template

Like I already pointed out you can use the auto-complete box with non-string data. This includes data that you want to present in a visually appealing way with more than just simple text entries. To this end, you can use the item template to specify how the items in the drop-down list will appear. Take the following simple model, for example:

 public class MyData
 {
     public Uri ImageUri
     {
         get;
         set;
     }
  
     public string Name
     {
         get;
         set;
     }
 }

To show the images referenced in these items, you can create an item template like the following:

 <sdk:AutoCompleteBox ItemsSource="{Binding Data}"
                      ValueMemberPath="Name"
                      FilterMode="ContainsOrdinal">
     <sdk:AutoCompleteBox.ItemTemplate>
         <DataTemplate>
             <StackPanel Orientation="Horizontal">
                 <Image Source="{Binding ImageUri}" />
                 <TextBlock Text="{Binding Name}"
                           VerticalAlignment="Center"
                           Margin="10 0 0 0" />
             </StackPanel>
         </DataTemplate>
    </sdk:AutoCompleteBox.ItemTemplate>
 </sdk:AutoCompleteBox>

An important detail to note here is the use of the ValueMemberPath property. When you work with complex types you need to tell the control what property should be used for a) the display in the text box, and b) in the filtering process for the items in the drop-down list. In this particular sample, we show both the image and name in the drop-down list, but in the text box and for the filtering, only the name property of our data class is used. Alternatively you can use the ValueMemberBinding property to use a binding instead of a property path, for example when you want to use a value converter. The result of the above example is something like this:

image

Using a custom filter method

In some cases, the built-in capabilities of the auto-complete box may not be sufficient for your scenario. Imagine a situation where you want to filter your list of source items by multiple property values. For example, users may want to search a database of electronic components by both the part name or the part number. Then you would have to compare the input of the text box to both properties to determine which possible candidates should be visible in the drop-down list. Luckily, the control offers two extension points for that too: the TextFilter and ItemFilter properties. Both offer the possibility to provide custom methods that are called to filter your items source. The first one passes in the string representation of the item to test, the second one in contrast passes in the original items used to bind the control in the first place. Let's see an example of how this works.

I'm using the same data class as in the sample before. This time, the XAML looks slightly different though:

 <sdk:AutoCompleteBox x:Name="MyAutoCompleteBox"
                      ItemsSource="{Binding Data}"                                 
                      ValueMemberPath="Name"
                      ItemFilter="{Binding FilterItem}">
     <sdk:AutoCompleteBox.ItemTemplate>
         <DataTemplate>
             <StackPanel Orientation="Horizontal">
                 <Image Source="{Binding ImageUri}" />
                 <TextBlock Text="{Binding Name}"
                            VerticalAlignment="Center"
                            Margin="10 0 0 0" />
             </StackPanel>
         </DataTemplate>
     </sdk:AutoCompleteBox.ItemTemplate>
 </sdk:AutoCompleteBox>    

Note that I bind the ItemFilter property to a property of my view model here. This property is just a simple wrapper around the actual filter method:

 public AutoCompleteFilterPredicate<object> FilterItem
 {
     get
     {
         return DoFilterItem;
     }
 }
  
 private bool DoFilterItem(string search, object data)
 {
     // get the original item
     var myData = data as MyData;
   
     if (myData == null)
     {
         return false;
     }
   
     // list the item if the name length is equal 
     // to the search term length
     return search.Length == myData.Name.Length;
 }

As you can see, the method signature for the filtering takes the search term and the current item to inspect as arguments. You need to return a bool value that determines whether the item will be included in the drop-down list (true) or not (false). In this case, I only return those items whose names have the same length like the search term. Not very meaningful, but it shows how you can do filtering the auto-complete box does not support out of the box. The formal documentation for the filter methods is described here.

Asynchronous filtering

One thing people frequently ask is how to do asynchronous filtering. A particular requirement often is to fetch possible candidates for the drop-down list from a web service. Doing this is surprisingly easy with the auto-complete box. To aid this, it offers two things. First of all there is the Populating event. It is raised when the population of the drop-down list is about to start. The important part is that you have the possibility to cancel the populating by setting the appropriate property of the event arguments in your event handler. This is the optimal place and way to start your asynchronous service call. The second piece of the puzzle is the PopulateComplete method. Call this method to signal to the auto-complete box that you are finished setting up the items source and that it can show the drop-down list now. In the end, this comes down to very little code, like this:

 private void MyAutoCompleteBox_Populating(object sender, PopulatingEventArgs e)
 {
     // create a web client and initiate the service call
     FilterServiceClient client = new FilterServiceClient();
     client.FilterCompleted += new EventHandler<FilterCompletedEventArgs>(ServiceClient_FilterCompleted);
     client.FilterAsync(e.Parameter);
  
     // cancel populating
     e.Cancel = true;
 }
   
 private void ServiceClient_FilterCompleted(object sender, FilterCompletedEventArgs e)
 {
     // set up the new items source
     // and tell the auto-complete box to finish populating
     MyAutoCompleteBox.ItemsSource = e.Result;
     MyAutoCompleteBox.PopulateComplete();
 }

As you can see, all that happens is the web service call is started in the populating event, and when it returns, the auto-complete box is told to finish the previously canceled populating. The corresponding XAML for this is:

 <sdk:AutoCompleteBox x:Name="MyAutoCompleteBox"
                     MinimumPopulateDelay="500"
                      Populating="MyAutoCompleteBox_Populating" />

Please note the use of the MinimumPopulateDelay property I've discussed above. To avoid that my service is being hit on every keystroke I've added a delay of 500 milliseconds here. This means that the populating event will not be raised more often than once per half second.

Bonus: MVVM-friendly asynchronous filtering

The way the above mentioned procedure works is not very MVVM friendly. You have the possibility to bind commands of your view model to events with built-in helpers like triggers and actions (or probably your MVVM framework has its own helpers for this); however, for the auto-complete box this is not enough. You need to change a property value of the very specific populating event arguments as well as call a method on the control when you have finished the filtering process. To avoid tight coupling between the view model and auto-complete box (as it would be introduced by passing references of event arguments or the control itself to your view model) I have written a behavior that acts as the glue between both. This only is one of many possibilities to handle this situation, but for my requirements so far it was sufficient.

First of all I created a parameter data container class to pass around the required information. In particular, the FilterCriteria property will contain the string the user entered into the text box. The FilterComplete property is an abstraction of the PopulateComplete method the auto-complete box offers.

 public class FilterAsyncParameters
 {
     public Action FilterComplete
     {
         get;
         private set;
     }
  
     public string FilterCriteria
     {
         get;
         private set;
     }
   
     public FilterAsyncParameters(Action filterComplete, string filterCriteria)
     {
         FilterComplete = filterComplete;
         FilterCriteria = filterCriteria;
     }
 }

Next I created a behavior that is typed to the AutoCompleteBox. This behavior has a dependency property of type ICommand that can be used to bind a command of the view model which should be executed when asynchronous filtering has to be performed.

 public class FilterAsyncBehavior : Behavior<AutoCompleteBox>
 {
     public ICommand FilterAsyncCommand
     {
         get
         {
            return (ICommand)GetValue(FilterAsyncCommandProperty);
         }
         set
         {
             SetValue(FilterAsyncCommandProperty, value);
         }
     }
                 
     public static readonly DependencyProperty FilterAsyncCommandProperty = DependencyProperty.Register("FilterAsyncCommand", 
         typeof(ICommand), 
         typeof(FilterAsyncBehavior), 
         new PropertyMetadata(null));
   
     ...

The behavior overrides the OnAttached and OnDetaching methods to add and remove an event handler for the populating event of the associated auto-complete box. In the event handler, the populating is canceled and the command on the view model is executed. The behavior creates a new instance of the custom parameters class to pass both the search string as well as the PopulateComplete method (disguised as FilterComplete action) to the view model.

 protected override void OnAttached()
 {
     base.OnAttached();
  
     // handle the populating event of the associated auto complete box
     AssociatedObject.Populating += AssociatedObject_Populating;
 }
                 
 protected override void OnDetaching()
 {
     // detach the event handler
     AssociatedObject.Populating -= AssociatedObject_Populating;
   
     base.OnDetaching();
 }
   
 private void AssociatedObject_Populating(object sender, PopulatingEventArgs e)
 {
     // get the command
     ICommand filterCommand = FilterAsyncCommand;
  
     if (filterCommand != null)
     {
         // create the parameters for the command
         var parameters = new FilterAsyncParameters(AssociatedObject.PopulateComplete, e.Parameter);
   
        // execute command
         filterCommand.Execute(parameters);
  
        // cancel the population of the auto complete box
         e.Cancel = true;
     }
 }

That's basically all. You can then use the behavior in XAML like this:

 <sdk:AutoCompleteBox x:Name="MyAutoCompleteBox"
                      MinimumPopulateDelay="500"
                      ItemsSource="{Binding Data}">                
     <interactivity:Interaction.Behaviors>
         <utilities:FilterAsyncBehavior FilterAsyncCommand="{Binding FilterAsyncCommand}" />
     </interactivity:Interaction.Behaviors>
 </sdk:AutoCompleteBox>

Of course your view model has to provide a property and some kind of implementation for the FilterAsyncCommand. In my case, I've used the RelayCommand class of MVVM light to relay execution to a method in my view model. The code is pretty similar to what we had in the code-behind file of the main page before, but of course without interacting with the auto-complete box directly.

 public MainViewModel()
 {
     FilterAsyncCommand = new RelayCommand<FilterAsyncParameters>(ExecuteFilterAsync);
 }
   
 private void ExecuteFilterAsync(FilterAsyncParameters args)
 {
     // start the async web service call
     FilterServiceClient client = new FilterServiceClient();
     client.FilterCompleted += new EventHandler<FilterCompletedEventArgs>(ServiceClient_FilterCompleted);
     client.FilterAsync(args.FilterCriteria, args);
 }
   
 private void ServiceClient_FilterCompleted(object sender, FilterCompletedEventArgs e)
 {
     // set the new data 
     Data = e.Result;
  
     // trigger the filter complete action
     var filterArgs = e.UserState as FilterAsyncParameters;
     filterArgs.FilterComplete();
 }

So, with little effort you can remove all code-behind even for asynchronous filtering and provide a more MVVM-friendly version of the sample.

Summary

Hopefully this article has helped you realize that with the AutoCompleteBox really comes great flexibility. The built-in features already cover a lot of the most common situations, and it offers various extension points for customization and to plug in your own filter logic, even for advanced scenarios when you want to fetch the potential matches from a web service. You can download the complete source code that contains all the sample projects here:

Download source code

About the author

Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions. In his free time he also works on hobby and commercial game projects. You can find his tech blog here: http://www.pitorque.de/MisterGoodcat/


Subscribe

Comments

  • -_-

    RE: Using the AutoCompleteBox


    posted by Mrunal Buch on Feb 07, 2011 07:52

    Great stuff !!!

    One of my requirement is let the user press the down arrow key initially and that brings out top 100 results. On pressing the down arrow key the execute method is never called. How can i implement this behavior. Thanks much again.

     

    Mrunal

  • -_-

    RE: Using the AutoCompleteBox


    posted by Peter Kuhn on Feb 07, 2011 11:55

    Hi Mrunal. Thank you for your comment.

    The problem with your approach most likely is that the text box handles the key down event and your custom event handler is never executed. You can solve this by adding a handler like this in code:


    KeyEventHandler eventHandler = MyAutoCompleteBox_KeyDown;
    MyAutoCompleteBox.AddHandler(KeyDownEvent, eventHandler, true);
    Note the last parameter (true), it is important. In the event handler, you can then e.g. start a service call or similar.


    Hope this helps.

  • -_-

    RE: Using the AutoCompleteBox


    posted by Aghdam on Feb 07, 2011 17:05
    awesome article , tank u
  • -_-

    RE: Using the AutoCompleteBox


    posted by Jim on Mar 01, 2011 17:24

    Hi Peter,
    Thanks or sharing your knowledge.  I am a newbie  in SL develpment.  I want to adopt your idea but I don't know how to implement if the AutoComplete shows the description but I need the Id of the record to be returned to my VM.

    In addition, I downloaded your  code and tried to run the MVVM one but getting "Sysem.Windos.Intertivity" could not be found.  Can you hlep?

    Jim

  • -_-

    RE: Using the AutoCompleteBox


    posted by Peter Kuhn on Mar 03, 2011 01:02

    Hi Jim. The System.Windows.Interactivity assembly is part of Expression Blend. If you don't have Blend, you can download the SDK which also contains these dlls here: Expression Blend 4 SDK.

    About returning the Id of the record to your VM: you can access the selected data item through the SelectedItem property. If you bind that property to a property of your VM you will have access to that item in your VM (and can then process the Id property of that item too).

  • -_-

    RE: Using the AutoCompleteBox


    posted by Swamy on Mar 10, 2011 00:54

    Great article .. ! Thanks you very much . .you have saved a lot of time for me ..

    In your example code, you explained MVVM filtering using web service/wcf service. How can I use it RIA. I was looking for the e.UserState in ria services ... no luck till now .. Any inputs on this would be great help.

  • -_-

    RE: Using the AutoCompleteBox


    posted by Peter Kuhn on Mar 10, 2011 10:05

    Hi Swamy. It works very similar with RIA Services too. The user state is part of the Load method overloads of the domain context there. For example, take this one:

    http://msdn.microsoft.com/en-us/library/ff422945(v=vs.91).aspx

    You can pass in the user state as last argument there.

  • -_-

    RE: Using the AutoCompleteBox


    posted by Kevin on Mar 11, 2011 14:03

    Hi I am hoping you can help me.

    I am having trouble with one of my Autocomplete boxes.

    When i download and run your project for the  ItemTemplat the dropdown works perfectly.

    When I copy all of your work into my application, the drop down behaves starnge.

    I can get the data populated and the Autocomplete seems to works however the Dropdown box simply displays a list of

    MyClass.Assets.MyData

    MyClass.Assets.MyData

    etc

     

    I have scoured to net looking for an answer to this and cant find one, Plaease help

    MyClass.Assets.MyData

    MyClass.Assets.MyData

  • -_-

    RE: Using the AutoCompleteBox


    posted by Peter Kuhn on Mar 11, 2011 16:01

    You need to set up your item template correctly so the included controls bind to properties that exist on your "MyData" class. 

    If you still have problems with that, feel free to contact me directly.

  • -_-

    RE: Using the AutoCompleteBox


    posted by Michel Miranda on Apr 30, 2011 17:16

    Very helpful! The only nice MVVM solution I found on the internet.

    I my MVVM application a details view is bound to the SelectedItem property of the AutoCompleteBox. But I don't want to receive selection notifications when the user is scrolling through the list of items using the up and down key. Only when the user closes the dropdown I want to update the details view. How can this be done using MVVM and behaviors?

    Thank you for your help

  • -_-

    RE: Using the AutoCompleteBox


    posted by Michel Miranda on Apr 30, 2011 18:12

    Very helpful! The only nice MVVM solution I found on the internet.

    I my MVVM application a details view is bound to the SelectedItem property of the AutoCompleteBox. But I don't want to receive selection notifications when the user is scrolling through the list of items using the up and down key. Only when the user closes the dropdown I want to update the details view. How can this be done using MVVM and behaviors?

    Thank you for your help

  • -_-

    RE: Using the AutoCompleteBox


    posted by Craig on May 04, 2011 19:53

    Just curious, why doesnt the AutoCompleteBox have an option to show the drop down arrow. Many times the user doesnt know which items are in the list. The drop down gives them an idea of what they can type. I have searched all around the internet and havent found a way to do this with the AutoCOmpleteBox, there are custom controls that half way work, but I think this should be a part of this control...maybe it is and I dont know it?

  • fabxcp

    Re: Using the AutoCompleteBox


    posted by fabxcp on Jan 17, 2012 03:01

    Very great stuff,

    the asynchronous filtering with a ViewModel sample is exactly what I was looking for. The samples are very very helpful for me.

    Thanks a lot for sharing your great knowledge.

  • weitzhandler

    Re: Using the AutoCompleteBox


    posted by weitzhandler on Jan 24, 2012 18:13

    Great post.

    Thanks for the MVVM bonus. That's what I came in for.

  • LakshmiNarayana

    Re: Using the AutoCompleteBox


    posted by LakshmiNarayana on Feb 01, 2012 08:40

    Hi,

    Great Stuff,

    I have used this in my project.

    I need selection start property like textbox, But autocomplete box don't have that type of property ?


  • weitzhandler

    Re: Using the AutoCompleteBox


    posted by weitzhandler on Feb 01, 2012 14:19

    @LakshmiNarayana, you could extract the TextBox from within the AutoCompleteBox if you'd like. Either search the Visual Tree, or subclass the ACB and expose the TB.

    Good luck, HTH,
    Shimmy

  • koolest

    Re: Using the AutoCompleteBox


    posted by koolest on Feb 22, 2012 09:49

    Thanks a lot Peter. Something we have been looking for. We had inherited from AutoCompleteBox to achieve some of these features. Your article has help us clear a lot of code.

  • koolest

    Re: Using the AutoCompleteBox


    posted by koolest on Feb 22, 2012 14:09

    I have got a problem here.

    I am using  "Asynchronous filtering" method to get data from server. The result from server is a DataView with 3 columns - FirstName, LastName, CompanyName.

    Whatever I am typing in AutoCompleteBox, server searches for that sub-string in all 3 fields i.e. FirstName, LastName, CompanyName.

    So for example if user searches for 'Microsoft'. Server returns result: John Players, Microsoft and Penny Schindler, Microsoft.

    It does not display anything as the ValueMemberPath=FirstName and FirstName does not contain the word 'Microsoft'. Is there a way to define multiple Property names in ValueMemberPath or just ignore it and display all data that is returned?

  • JaspreetSaini

    Re: Using the AutoCompleteBox


    posted by JaspreetSaini on May 11, 2012 09:33

    In my project i needed an autocomplete of symbols also. For example @,#

    I am not getting the solution for this.

  • ItamarAsis

    Re: Using the AutoCompleteBox


    posted by ItamarAsis on Nov 20, 2013 12:34

    Very helpfull article. Thanks....

Add Comment

Login to comment:
  *      *