WPF AutoSuggest Textbox

This article is written by Pon Saravanan  on 30-Mar-10 Last modified on :30-Mar-10

AutoSuggest for a WPF Textbox

Auto suggest feature is increasingly popular because of the ease of use. In ASP.Net we can use the Ajax tool kit to get the suggestions easily. But I couldn’t locate one for WPF applications. So I started writing my own. Though the source code is not written completely to cater the rich user friendliness, the code is still usable with or without some small changes.

Suggestions from Database

As I have mentioned bit earlier that the suggestions are expected to be available from a database table. So we are going to get the data into a DataTable. For easier explaining, I usually choose Northwind as the database and this time I have chosen customer as the table for samples.

Note: As it is already explained several times in earlier articles, I am not going to explain again how to get the data and how to read the data from database into a dataset/DataTable. So refer to the Asp.net DataControls section or GridView section for that matter

TextChanged Event for filtering the data

For this sample I have decided to fetch all the records into DataTable in a class level variable. So that later on TextChanged event we can just use the DataView to filter the rows as per the data entered in the Textbox. In the TextChanged event we are not going to fetch the data from database. Rather we are going to use the filtered data which is in DataTable. For filtering data we can use the RowFilter property of the DataView to provide the filtering criteria.

Suggestion in WPF ListBox

I have opted to use ListBox for displaying suggestions for the letters typed in the textbox. The filtered DataView can be passed as a DataSource using the property DataContext. DataTemplate can be used to format the results in the ListBox. To read more about ListBox and its DataBinding refer the article WPF DataBinding in the WPF articles Section

Show Hide suggestions

As we see suggestions all over different applications, we can figure out that one of the integration functionality is hiding the suggestions after sometimes. For this behavior we are using a separate thread and a thread.sleep for giving the delay before hiding the suggestions. Since the hiding logic is going to be in a separate thread, accessing the ListBox which is in the main thread will cause you the cross threading exception. To avoid that, use a delegate and pass the ListBox as an argument.

Selection of Suggestions

The selection is possible through the mouse click. But it is nice to use keyboard as well for navigate the suggestions. Navigating all the suggestions using keyboard is currently not concentrated, however you can still use the down arrow key to navigate to the first suggestion.

Screen Shot

Source Code


<Window x:Class="Sample"
    Title="Window1" Height="300" Width="563">
        <DataTemplate x:Key="CustomerTemplate">
            <Border BorderThickness="1" BorderBrush="silver" CornerRadius="5"
                    Padding="2" Margin="2px" Width="490" Background="#E6E6E6"
                    HorizontalAlignment="Center" VerticalAlignment="Center">
                <Grid FlowDirection="LeftToRight" >
                    <TextBlock Text="{Binding CompanyName}" 
                        Foreground="#515151" FontSize="12"
                        HorizontalAlignment="Left" FontWeight="Bold" />
        <TextBox Height="23" Margin="10,10,20,0" Name="TextBox1"
                  VerticalAlignment="Top" />
        <ListBox  Name="ListBox1" ItemsSource="{Binding}" 
                  ItemTemplate="{StaticResource CustomerTemplate}"

Code Behind

Imports System.Data
Imports System.Threading
Imports System.Data.SqlClient
Imports System.Windows.Threading
Class Sample
    Private Customers As DataTable
    Delegate Sub ProcessListBox(ByVal CurrentListBox As ListBox)
    Public MyListBoxProcessor As New ProcessListBox(AddressOf DoProcessOnListBox)
    Private Sub Window1_Loaded(ByVal sender As Object, _
                               ByVal e As System.Windows.RoutedEventArgs) _
                               Handles Me.Loaded
        MyListBoxProcessor = New ProcessListBox(AddressOf DoProcessOnListBox)
        ListBox1.Visibility = Windows.Visibility.Hidden
        Customers = GetData().Tables(0)
    End Sub
    Private Sub TextBox1_PreviewKeyDown(ByVal sender As Object, _
                                    ByVal e As KeyEventArgs) _
                                    Handles TextBox1.PreviewKeyDown
        If (e.Key = Key.Down) Then ListBox1.Focus()
    End Sub
    Private Sub TextBox1_TextChanged(ByVal sender As System.Object, _
                                     ByVal e As TextChangedEventArgs) _
                                     Handles TextBox1.TextChanged
        ListBox1.Visibility = Windows.Visibility.Visible

        Dim HideThread As New Thread(AddressOf HideListBox)
    End Sub
    Private Sub ListBox1_SelectionChanged(ByVal sender As Object, _
                                      ByVal e As SelectionChangedEventArgs) _
                                      Handles ListBox1.SelectionChanged
        If (ListBox1.SelectedItem Is Nothing) Then Exit Sub
        TextBox1.Text = DirectCast(ListBox1.SelectedItem, DataRowView)("CompanyName")
        ListBox1.Visibility = Windows.Visibility.Hidden
    End Sub
    Private Sub HideListBox()
        Dispatcher.Invoke(MyListBoxProcessor, ListBox1) 'perfom crossthread
    End Sub
    Private Sub BindData()
        Dim CustomerView = Customers.DefaultView
        CustomerView.RowFilter = "CompanyName like '%" & TextBox1.Text & "%'"
        ListBox1.DataContext = CustomerView
    End Sub
    Private Function GetData() As DataSet
        Dim NwndConString As String = "Data Source=.\SQLEXPRESS;" & _
                "Initial Catalog=Northwind;Persist Security Info=True;" & _
                "Trusted_Connection=True "
        Dim conNwnd As New SqlConnection(NwndConString)
        Dim strSelect As String = "Select * from Customers"
        Dim adaNwnd As New SqlDataAdapter(strSelect, NwndConString)
        Dim dsNwnd As New DataSet
        Return dsNwnd
    End Function
    Private Sub DoProcessOnListBox(ByVal CurrentListBox As ListBox)
        CurrentListBox.Visibility = Windows.Visibility.Hidden
    End Sub
End Class

« Previous -

    This is fine as long as the DB is local and the Customer table relatively small. If you were looking for products from say Amazon, you should allow for a rapid typist so every keystroke does not the database. Try an queue with a delay where all the queries except the one caused by last keystroke are ignored. 4/6/2010 11:48:57 AM

  • pons
    For the scope of this article queueing may confuse the beginners. As i mentioned earlier in the article, this code is just a start up for auto suggest. There are possibly a ton of functionalities to be added for real time usage. There may be some restriction on minimum characters before apply filtering (as in Ajax Tool kit).

    Are you sure Amazon Product services API allows you to get products in bulk manner, i am using it currently on another websites it is returning only 10 records per query.

    But your point is noted, and possibly i will add a enhancements section there i can add those items.
    4/6/2010 6:59:26 PM

    You may want to check this one here: http://code.google.com/p/kocontrols/downloads/list it has delay timer and much more. 11/26/2011 1:55:07 PM

Captcha Image
For you specially:  
Captcha Text Enter the text in the image.(Not Case sensitive)    

Spam Bot Trap


Select Theme