Click here to Skip to main content
15,867,966 members
Articles / Desktop Programming / WPF
Tip/Trick

Filtered DropDown ComboBox

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
2 Jun 2012CPOL 36K   5   2
A rather quick and dirty way of creating a filtered combo box in WPF.

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:

C#
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:

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


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
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 eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
SuggestionArticle is not very useful in this shape Pin
WojciechD9-Oct-14 0:41
professionalWojciechD9-Oct-14 0:41 
QuestionIssues Pin
Guillaume Leparmentier3-Jun-12 0:44
Guillaume 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 :

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

XML
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:10am.

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.