Silverlight AutoCompleteBox

by Bron Skinner 13. April 2010 11:58

In looking online it would appear there are already a couple AutoCompleteBox examples floating about - Example.

However, in my usual spirit I’ve decided to tackle the problem myself and have come up with (albeit rough, but only lacking tweaking), a pretty cool implementation of my own Silverlight AutoCompleteBox.

Silverlight AutoCompleteBox

An  AutoCompleteSearchBox class takes an ObservableCollection<T> of source items (a complete listing of items to search), and stores this as well as a PropertyPath with which to search by.

public class AutoCompleteSearchBox<T> : ObservableCollection<T>

    {

        private ObservableCollection<T> ItemSource { get; set; }

        private PropertyPath SearchProperty { get; set; }

        public AutoCompleteBox ACBox;

 

        public AutoCompleteSearchBox(ObservableCollection<T> itemSource, PropertyPath searchProperty)

        {

            SetItemSource(itemSource, searchProperty);

        }

An AutoCompleteBox class builds and returns a simple Grid containing a TextBlock and ListBox as well as attaching our various events and providing a means for updating the ListBox ItemsSource.

public class AutoCompleteBox

    {

        public TextBox SearchField;

        public ListBox ResultsField;

        public Grid LayoutRoot;

        public AutoCompleteResult SelectedResult = new AutoCompleteResult(string.Empty, -1);

You’ll notice a third type reference AutoCompleteResult. This is used to store any search matches in a predictable format and is the type our ListBox’s ItemTemplate is expecting when we set its ItemsSource.

public partial class AutoCompleteResult

    {

        public string Display { get; set; }

        public int Index { get; set; }

 

        public AutoCompleteResult(string display, int index)

        {

            Display = display;

            Index = index;

        }

    }

Our ResultsField (ListBox) which displays matches found uses a simple DataTemplate binding a single TextBlock’s Text to the Display Property of our AutoCompleteResult type.

  ResultsField = new ListBox();

            ResultsField.ItemTemplate = (DataTemplate)XamlReader.Load("<DataTemplate xmlns='http://schemas.microsoft.com/client/2007' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'><StackPanel><TextBlock Text='{Binding Path=Display, Mode=OneWay}' /></StackPanel></DataTemplate>");

            ResultsField.MouseEnter += new MouseEventHandler(ShowResultsField);

            ResultsField.MouseLeave += new MouseEventHandler(HideResultsField);

            ResultsField.MouseLeftButtonDown += new MouseButtonEventHandler(ResultsField_MouseLeftButtonDown);

            ResultsField.Visibility = Visibility.Collapsed;

Notice as well the MouseEvents which allow us to influence how our results show and hide as we MouseEnter, MouseLeave, KeyDown etc.

To enable our search filtering we attach a SearchField_TextChanged eventhandler to our TextBlock’s TextChanged event.

protected void SearchField_TextChanged(object sender, RoutedEventArgs e)

        {

            ACBox.SetResults(FilterResults(((TextBox)sender).Text));

        }

This sets our ResultsField (ListBox) ItemsSource to the found matches from our source items converted to AutoCompleteResults.

The Actual filtration process (FilterResults above) uses an extremely simple implementation of RegularExpressions to compare string matches in each of our source items against the initially supplied PropertyPath:

public List<AutoCompleteResult> FilterResults(string input)

        {

            List<AutoCompleteResult> Matches = new List<AutoCompleteResult>();

 

            try

            {

                string pat = input;

                Regex r = new Regex(pat, RegexOptions.IgnoreCase);

                for (int q = 0; q < ItemSource.Count; q++)

                {

                    PropertyInfo pi = ItemSource[q].GetType().GetProperty(SearchProperty.Path);

                    if (pi != null)

                    {

                        Match m = r.Match(pi.GetValue(ItemSource[q], null).ToString());

                        if (m.Success)

                        {

                            Matches.Add(new AutoCompleteResult(pi.GetValue(ItemSource[q], null).ToString(), q));

                        }

                    }

                }

            }

            catch(Exception ex)

            {

 

            }

            return Matches;

        }

Finally, we’ll want to do something with our selected result (typeof(T)). Our AutoCompleteBox’s SelectedResult property stores the last selected AutoCompleteResult from our ResultsField (ListBox.SelectedItem). A few checks to ensure that the SelectedResult isn’t outside the scope of our item source and vuala.

public T GetSelectedResult()

        {

            if (ACBox.SelectedResult != null && ACBox.SelectedResult.Index >= 0 && ACBox.SelectedResult.Index < ItemSource.Count)

            {

                return ItemSource[ACBox.SelectedResult.Index];

            }

            return default(T);

        }

Conclusions:

A pretty simplistic implemenation I'd like to see the ability to filter by non-string properties as well as supply custom regular expressions with which to filter against. I've not tested this control against extremely large lists and would like to, instead of looping through our source items, implement LINQ.

Tags:

Technical

Comments

6/29/2010 10:16:35 PM #

Very interesting post I have seen here.Thanks for posting it.

scabies treatment United States |

6/30/2010 1:43:44 PM #

Thanks for such a nice article.It includes very informative information about the article.

fleas on humans United States |

6/30/2010 5:54:15 PM #

found your site on del.icio.us today and really liked it.. i bookmarked it and will be back to check it out some more later ..

Prison Break TV United States |

Powered by BlogEngine.NET 1.5.0.7
Custom theme for digitalboon.com by Bron Skinner

About the author

A Software Developer with a keen artistic sense, I’ve spent the last couple years working with predominantly Microsoft-based technologies developing web applications. The majority of this time has been spent building applications with SilverlightTM that forward some rather unique approaches to interface design. I am currently working full time.

 

 

Make sure to check out The Forge.


Download Resume