Click here to Skip to main content
Click here to Skip to main content

Filtered DropDown ComboBox

By , 2 Jun 2012
 

I have discovered a rather quick and dirty way of creating a filtered combo box in WPF. It has some quirks, but it is good for longer lists. One issue I discovered was that if I was using the same list for two combo boxes they would interfere with one another. Here is the code behind to handle with combo box:

comboBox1.Loaded += delegate
{
  var textBox1 = (TextBox)comboBox1.Template.FindName(
    "PART_EditableTextBox", comboBox1);
  var popup1 = (Popup)comboBox1.Template.FindName("PART_Popup", comboBox1);

  textBox1.TextChanged += delegate
  {
    popup1.IsOpen = true;
    comboBox1.Items.Filter += a =>
    {
      return textBox1.Text.Length == 0 || (a.ToString().ToUpper().
        StartsWith(textBox1.Text.
        Substring(0, Math.Max(textBox1.SelectionStart, 1)).ToUpper()));
    };
  };
};

In the example I have two combo boxes because I needed two and I was getting some strange behavior between the two. Still get some strange behavior that somebody may be able to figure out.

To do two text boxes I have duplicated code for the lambda expressions. I needed to do this because need to have the parent combo box information when dealing with the TextChanged event. I was unable to figure out how to not duplicate code without some side effects.

The XAML for the form is the following:

<Window x:Class="FilteredComboBoxExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:FilteredComboBoxExample"
        Title="Filtered DropDown ComboBox" Height="200" Width="200">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Width="80">
    <ComboBox Grid.Column="2"
              Grid.Row="3"
              Name="comboBox1"
              IsEditable="True"
              ItemsSource="{Binding Source={x:Static local:StaticData.GetList}}" />
    <ComboBox Grid.Column="2"
              Grid.Row="4"
              Name="comboBox2"
              IsEditable="True"
              ItemsSource="{Binding Source={x:Static local:StaticData.GetList}}" />
  </StackPanel>
</Window>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Clifford Nelson
Software Developer (Senior) ETeam/Delloitte
United States United States
Member
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last three years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with PIMCO in Newport Beach, CA.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionIssues [modified]memberGuillaume Leparmentier3 Jun '12 - 0:44 
Hi Clifford,
 
I think I've found why your code behaves strangely.
 
In your code, when you register "Loaded" event, I hope you didn't perform a "basic" copy/paste without changing the name of targetted combobox Smile | :) (ie. comboBox1 and comboBox2)
 
When I've tested your code, it was always the same popup that pops out.
 

To be sure to use the good nested controls (Textbox and Popup) of targetted combo, I've changed a bit you code like this :
 
public MainWindow()
{
    InitializeComponent();
 
    comboBox1.Loaded += new RoutedEventHandler(InitCombo);
    comboBox2.Loaded += new RoutedEventHandler(InitCombo);
}
 
private void InitCombo(Object sender, RoutedEventArgs e)
{
    var combo = (ComboBox)sender;
    var textBox = (TextBox)combo.Template.FindName("PART_EditableTextBox", combo);
 

    // register change filtering
    textBox.TextChanged += delegate(Object t, TextChangedEventArgs tea)
    {
        var tb = (TextBox)t;
        var cb = FindVisualParent<ComboBox>(tb);
 
        // invoke filtering
        cb.Items.Filter += delegate(Object value)
        {
            return tb.Text.Length == 0 || (value.ToString().ToUpper().StartsWith(tb.Text.Substring(0, Math.Max(tb.SelectionStart, 1)).ToUpper()));
        };
 

 
        // open popup
        var pp = (Popup)cb.Template.FindName("PART_Popup", cb);
        if (pp != null) pp.IsOpen = true;
 
    };
}
 
The FindVisualParent() Method is the following one:
 
public static T FindVisualParent<T>(DependencyObject child) where T : DependencyObject
{
    // get parent item
    DependencyObject parentObject = VisualTreeHelper.GetParent(child);
 
    // we’ve reached the end of the tree
    if (parentObject == null) return null;
 
    // check if the parent matches the type we’re looking for
    T parent = parentObject as T;
    if (parent != null)
    {
        return parent;
    }
    else
    {
        // use recursion to proceed with next level
        return FindVisualParent<T>(parentObject);
    }
}
 

Hope this helps Smile | :)
Guillaume

modified 3 Jun '12 - 7:10.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 2 Jun 2012
Article Copyright 2012 by Clifford Nelson
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid