← About Jeff

Customizing AutoCompleteBox to update SelectedItem on commit, without changing the control

January 20, 2009

This post presents a quick story about some of the process we use on the Silverlight Toolkit, plus a downloadable project that shows how to customize and extend AutoCompleteBox to only update the SelectedItem when the user commits their suggestion choice (enter key or clicking an element) instead of when the up/down arrow keys are used to cycle through the suggestions.

Community feedback is key – make your vote count!

The Issue Tracker page on CodePlex is an important tool for us to collect feedback from, and we’ve already taken to heart the issues and requests that you’ve reported and voted on. One of the most-requested items last month was a request that started in the Silverlight.net forums and became a request on CodePlex. We implemented this change in preparation for the next release, and I even went ahead and blogged the design change request (DCR) and the updated source code at that time.

We’ve also committed to sharing our process, stories, and guidance as we all work together on making the Silverlight ecosystem a better place.

Yesterday a feature request was logged on CodePlex by a customer that would prefer a different behavior for the AutoCompleteBox control: they’d prefer that SelectedItem not be updated when you press the arrow keys to move through the suggestions list. Thanks for the request, Roetje.

What I’ve done

I went ahead and solved the problem without changing the AutoCompleteBox control. Perhaps to validate the ISelectionAdapter interface that I designed for the control, or just to fill the evening, I geek’d out a little bit. I’ve enclosed the solution a little farther down the page.

I did this by creating a custom selection adapter that is a ContentControl. The adapter modifies the default key down handling logic that ships with the toolkit by ignoring selection updates when the up/down keys are pressed. This is also a great demonstration of custom selection adapter creation since it wraps another control inside a custom template.

What we might do

Our program management team reviews the new bugs, feature requests, and other action we see in the community on a daily basis. Just earlier today, I was pulled into a quick triage meeting to chat about some open issues.

Since this issue was only just opened hours ago, I’m going to need to sit down with my program manager and expert tester to discuss this request. We might decide to investigate it further, gathering evidence from customers, partners, and friends. We may decide to work to update the specs, come up with a design for a feature to expose an on/off dependency property for the control. We might reject and close the request, if we feel that there’s an appropriate workaround, easy-to-customize solution.

Why not expose another property?

I’m leaning towards not adding a new property, but that’s only my opinion, and sad to say, that isn’t a trump card. I understand this is a great request, something that is not supported “out of the box,” but am also a little weary to check out the latest source from our Team Foundation Server and prepare a new feature. I’m much happier to build a sample application demonstrating an alternative solution.

Remembering some of the demanding clients I worked with in my freelancing days, I’d often get frustrated when official controls didn’t support every little knob and dial that I wanted to customize. Hindsight doesn’t count for much, but today I realize that it is important that the controls that we offer at Microsoft appeal to a broad audience, are fun to use, customizable & extensible, of extremely high quality, and all while meeting the stringent quality gates, code coverage, source code analysis rules, and other things we set out to do.

Plus, there’s only so much we can offer in the default experience: there are so many professionals in the industry developing much more customizable, “jack-of-all-trades”, and specialized control and application frameworks for WPF and Silverlight. And they’re the folks that should be doing it; I don’t know anything about writing a page-turning book control for Silverlight, but am glad that someone does and has.

Unlike Windows Forms, with WPF and the presentation framework subset in Silverlight, there are so many customizations that can be done by tweaking the screenfulls of FrameworkElement, UIElement, and Control properties. And then there is the ever-important way of life with WPF: retemplate, and retemplate often. No longer do you have to write custom owner drawing!

We also risk affecting performance, cluttering IntelliSense & Expression Blend, introducing complex or buggy logic, and really just making a mess of things when the urge for new properties comes along.

Studying auto complete in the wild

The last point I’d like to make is that I’m not an expert on auto complete controls. I really like the one we’ve built, but that doesn’t mean it is going to take over the world. We worked through many compromises, limitations, fun ideas, and made some calls to yield a good control that works in so many places, but isn’t overburdened with functionality.

One of the key fact-finding missions that I set out on with Mehdi (the control’s program manager) before even starting to design the control was to see just how many auto complete controls we could find. In short, there are a lot – any so many of them are very different. Once you find an auto complete control, its easy to spend 20 minutes studying the intricacies of the navigation model, performance, visual appearance, possible implementation ideas, and a wish list. Mehdi had a great set of core features that he wanted to see us tackle, and we had some good discussions about how to bring those to Silverlight, offer our customers options, and also deliver the control with a high level of quality very quickly, to make the first toolkit release in November.

There were a lot of small differences and questions we had: do we support page up and page down? lists are boring – can we put richer controls inside the drop down with suggestions? data templates should work, right? do we want cyclic navigation or not? can you customize filters? if so, are customized filters working with strings or with items? can we enable cool highlighting like the best auto complete controls have?

I’ve very happy with what we ended up with: a core control that really just works most of the time, offers extension points, a new selection adapter concept for moving navigation and the suggestions list out of the control for customization, good performance, and open source code.

A custom selection adapter

So, back to the CodePlex request, I quote:

When a user navigates through the DropDown of the AutoCompleteBox, the Selection of the AutoCompleteBox is changed, and thus the text is changed.

I would like to be able to control this behavoir. Because when I search and I navigate through the list, I still want to be able to continue with my SearchText, even when I have an item selected in the DropDown.

Here’s what I’m going to do:

  • Create a new Silverlight Application project, adding a reference to the December 2008 release of the Silverlight Toolkit
  • Pull in ScrollExtensions.cs and VisualTreeExtensions.cs from the toolkit source (they’re used by the selection adapter I’m starting with)
  • Copying SelectorSelectionAdapter.cs into my new app project and renaming it to ExampleSelectionAdapter
  • Creating a new template for the control in Page.xaml

The modifications to the standard shipping selection adapter:

  • Making the adapter into a ContentControl, to enable us to place it in XAML
  • Overriding OnApplyTemplate to grab the content (the ListBox)
  • Updating the Key.Enter handler to fire the OnSelectionChanged event
  • Updating the Key.Down and Key.Up handlers to temporarily ignore the selection change event, using the already-present ‘IgnoreSelectionChanged’ bool property of the adapter.

Here’s the updated HandleKeyDown method:

/// <summary>
/// Provides handling for the KeyDown event that occurs when a key is 
/// pressed while the control has focus.
/// </summary>
/// <param name="e">The key event arguments object.</param>
public void HandleKeyDown(KeyEventArgs e)
{
    switch (e.Key)
    {
        case Key.Enter:
            OnSelectionChanged(this, new SelectionChangedEventArgs(new List<object>(), new List<object> { SelectedItem }));
            OnCommit();
            e.Handled = true;
            break;

        case Key.Up:
            IgnoringSelectionChanged = true;
            SelectedIndexDecrement();
            IgnoringSelectionChanged = false;
            e.Handled = true;
            break;

        case Key.Down:
            if ((ModifierKeys.Alt & Keyboard.Modifiers) == ModifierKeys.None)
            {
                IgnoringSelectionChanged = true;
                SelectedIndexIncrement();
                e.Handled = true;
                IgnoringSelectionChanged = false;
            }
            break;

        case Key.Escape:
            OnCancel();
            e.Handled = true;
            break;

        default:
            break;
    }
}

And, again, this is nice because the custom selection adapter lives in your app, but you don’t have to touch the core AutoCompleteBox control at all, and can just use the shipped Toolkit binaries.

I’m not going to paste the entire custom template in here, but the key thing to notice is that the part named SelectionAdapter is the custom, local class that derives from ContentControl and implements ISelectionAdapter; it contains the ListBox:

<local:ExampleSelectionAdapter 
    x:Name="SelectionAdapter"
    HorizontalContentAlignment="Stretch"
    VerticalContentAlignment="Stretch">
    <ListBox 
        x:Name="SelectionAdapterListBox" 
        ScrollViewer.HorizontalScrollBarVisibility="Auto" 
        ScrollViewer.VerticalScrollBarVisibility="Auto" 
        ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
        ItemTemplate="{TemplateBinding ItemTemplate}" />
</local:SelectorSelectionAdapter>

Download the bits

Download the app [Zip, 67 KB]
Download ExampleSelectionAdapter.cs [Cs, 11 KB]

Hope this helps!