Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Omer van Kloeten's Facebook profile

Omer has been professionally developing applications over the past 8 years, both at the IDF’s IT corps and later at the Sela Technology Center, but has had the programming bug ever since he can remember himself.
As a senior developer at NuConomy, a leading web analytics and advertising startup, he leads a wide range of technologies for its flagship products.

Get Firefox


powered by Dapper 

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

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 :)

Comments

Jason Haley said:

# November 12, 2007 10:13 AM

Christopher Steen said:

Link Listing - November 12, 2007

# November 13, 2007 6:42 AM

Christopher Steen said:

ASP.NET First Line of Defense for Web Applications – Part 4 [Via: techjunkie ] ASP.NET Applications...

# November 13, 2007 6:43 AM

sam said:

this is cool, I like the code

# January 15, 2008 12:35 PM

Richard jablonski said:

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

# January 22, 2008 11:42 PM

Omer van Kloeten said:

I doubt it very much that this can be done with XAML only and no code.

# January 23, 2008 3:29 AM

David Vu said:

Do you have a WPF sample application that uses this customized control?

# March 14, 2008 2:03 PM

Omer van Kloeten said:

Sorry, no, but you can simply use it like any other combo box. Just replace the usual name with the control's name and voila. :)

# March 14, 2008 3:06 PM

Jimmy V said:

# March 30, 2008 10:01 PM

Omer van Kloeten said:

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."

# March 31, 2008 1:36 AM

capser said:

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?

# May 5, 2008 9:06 AM

Omer van Kloeten said:

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 :)

# May 5, 2008 2:18 PM

archimed7592 said:

Not working though. Neither with GetTemplateChild nor with Template.FindName...

# October 24, 2008 4:56 PM

Sachet said:

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.

# October 26, 2008 12:12 PM

JTango said:

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.

# November 18, 2008 11:20 PM

Jeff Schwandt said:

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

<code>

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);

</code>

# December 3, 2008 10:52 AM

Daniel said:

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.

# February 20, 2009 8:49 AM

Omer van Kloeten said:

Daniel,

Since it's been well over a year since I stopped doing WPF work and changes jobs, I unfortunately can't find the solution itself to attach. Sorry.

# February 20, 2009 11:15 AM

Hola said:

Sabe alguien hacerlo para un listview.

# March 6, 2009 9:05 AM

Nimrand said:

"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.

# April 2, 2009 9:50 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)