Automatically Filtering a ComboBox in WPF

Haven’t you always wanted to use a combo box like the one in Start menu’s Run dialog?

afcb

A client asked me for the exact same thing, and now you can download it from here too. The Automatically Filtered ComboBox inherits from the original ComboBox and adds the auto-filtering, along with an ability to filter while ignoring case. The IsTextSearchEnabled property already allows you to do all this (and the use of which is therefore supported), but it doesn’t allow for case sensitivity and filtration of the results.

Interesting bits about the code:

/// <summary>
///
Gets the text box in charge of the editable portion of the combo box.
/// </summary>
protected TextBox EditableTextBox
{
get
{
return ((TextBox)base.GetTemplateChild("PART_EditableTextBox"));
}
}

The above section of code lets you access the text box that sits at the top of the combo box. We use this text box to get the selection (just like what we could get from a ComboBox in Windows Forms using the SelectedText property).

ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
view.Filter += this.FilterPredicate;

ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.Refresh();

The above two piece of code use the CollectionViewSource class to create a filter on the default view of the ItemsSource. This mechanism is used internally by the original ComboBox to filter and/or sort the source of the items. I’m a bit baffled by why the WPF team would decide on only using a single, global view for the items’ source, rather than a model like the one presented in ADO.NET using the DataView on DataTables. Thanks to Tomer for  the heads up on that one.

private bool FilterPredicate(object value)
{
// We don't like nulls.
if (value == null)
return false;

// If there is no text, there's no reason to filter.
if (this.Text.Length == 0)
return true;

string prefix = this.Text;

// If the end of the text is selected, do not mind it.
if (this.length > 0 && this.start + this.length == this.Text.Length)
{
prefix = prefix.Substring(0, this.start);
}

return value.ToString()
.StartsWith(prefix, !this.IsCaseSensitive, CultureInfo.CurrentCulture);
}

The above is the predicate for filtering. As you can see, only items that start with the text already in the box are kept in the list (Yes, value.ToString() is not a correct implementation; I will change it in the future). Notice that if the selection is on the end of the text, we do not use that part for the search, since the very next character pressed will replace the selected text and we would like to show the possible results for such a keystroke.

As I am still in the process of getting used to WPF, there is still much to learn. I would gladly accept any comment you may have about the means to the end and also on my style and conventions. You always have something new to learn :)

Advertisements

28 thoughts on “Automatically Filtering a ComboBox in WPF

  1. Hi Sounds cool, but Im new to WPF and wondering how to implement this class with a combobox declared in XAML. thx in Advance

  2. FTA: “The IsTextSearchEnabled property already allows you to do all this (and the use of which is therefore supported), but it doesn’t allow for case sensitivity and filtration of the results.”

  3. Great stuff :)
    This code appears to throw in the getter for EditableTextBox if you are in the VS designer.
    One possible workaround is to wrap the addition of the event handler and check to see if (this.EditableTextBox != null) first.
    Also, should we be using
    return Template.FindName(“PART_EditableTextBox”, this) as TextBox;
    instead of GetTemplateChild?

  4. Hey casper,
    Thanks for the comment. It’s been a while since I touched the code, so maybe there are a few things I have missed. I hope whomever decides to use the code reads your comments and tries your suggestions, at the moment, I’m too far from WPF development to answer :)

  5. Hi,
    I used the autofiltercombobox given here but i am not able to display the filtered item list on a popup like you have shown in the snapshot above. Can you please tell me how i can display a popup below the combobox which contains a filtered list of items based on the text entered.

  6. Casper & Archimed7592, make sure you have the IsEditable property set to true in your XAML otherwise the retrieval of the template will fail since there is no TextBox to get.

  7. Regarding the ToString() thing, I used a bit of reflection to get the value of the property identified by DisplayMemberPath:

    string textValue = string.Empty;
    if (!string.IsNullOrEmpty(this.DisplayMemberPath))
    {
    Type t = value.GetType();
    var displayMemberPathProperty = t.GetProperty(this.DisplayMemberPath);
    if (displayMemberPathProperty != null)
    {
    textValue = displayMemberPathProperty.GetValue(value, null).ToString();
    }
    }
    if (textValue.Length == 0)
    textValue = value.ToString();
    return textValue.StartsWith(prefix, !this.IsCaseSensitive, CultureInfo.CurrentCulture);

  8. May I suggest that you include the solution instead of the mere file? It would save a lot of people the time of trying to get the sample to work and would be a proof of consept.
    I could not get it to work myself, as I’m a bit new in the WPF terrain, have to research some more I guess.

  9. “I’m a bit baffled by why the WPF team would decide on only using a single, global view for the items’ source, rather than a model like the one presented in ADO.NET using the DataView on DataTables. Thanks to Tomer for the heads up on that one.”
    You don’t have to use the default view. You can create your own collection view, usually in xaml, and then bind to it. However, if you just bind directly to the collection, then the combo box will automitcally use the default view.

  10. JTango alluded to this already but I have found that after struggling with exceptions with the code that there are pre-requisites in the Xaml use of this control that were invisible as the author only published the *.cs file.
    I have found that in order for it to work as described by the auther both IsEditable (required to get the correct template for a ComboBox as the default template does not contain a Part_EditableTextbox), and IsTextSearchEnabled must be set to true. Also for the filtering to be considered the control must be using ItemsSource.
    See workable Xaml below.

    Simon

  11. Dude, I have been messing around with this stupid file for the last hour. Either put up a damn solution (SLN) or take this crap down so as not to waste people’s time.

  12. I have tried this with VS2008 and cannot get it to work as shown in your screenshot above. I have set IsEditable to true and IsTextSearchEnabled to false and also tried some other variants. I have also tried to incorporate the fixing code by another commenter above. It simply does not work. I think, there must have been more code than what you are publishing here. Therefore, this page is rather confusing and should be removed.

  13. I have tried this with VS2008 and cannot get it to work as shown in your screenshot above. I have set IsEditable to true and IsTextSearchEnabled to false and also tried some other variants. I have also tried to incorporate the fixing code by another commenter above. It simply does not work. I think, there must have been more code than what you are publishing here. Therefore, this page is rather confusing and should be removed.

  14. When I type into my combobox the CPU just goes nuts. Anyone experience any strange loops when implementing this code? I havent altered it at all. My itemsSource has about 450 elements.

  15. If you want to use this in a WPF application AVOID!!!!!! Shame it mention WPF in the title.

  16. Hi,
    What should i write in xaml to implement this functionality, means what properties i need to set in xaml ?

    • Hi Clifford,

      As you can see from the date on this blog post, this was done several years ago with a very old version of WPF. I’m pretty sure most of the hacks I posted to this blog would not work by now, since they work based on undocumented behavior.
      However, you can take this as inspiration and try to change whatever it is that didn’t work for it to work.

      Omer

  17. Hi,
    Thanks for this great post.
    I tried to get your code worked with DataTemplate, instead of DisplaMemberPath but failed. Could you help me in this regard?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s