C# Adding Filter to combobox dropdown list

C# Adding Filter to combobox dropdown list

有个bug

Need some help with adding filter to my ComboBox drop down list(windows Forms Visual studio 2015)

The drop down is populated as per below:

public ReconciliationReport(){
    InitializeComponent();
    AppDomain.CurrentDomain.AssemblyResolve += FindDLL;

    this.sRootDirectory = Properties.Resources.sRootDirectory;

    string[] arrProjectList = Directory.GetDirectories(sRootDirectory).Select(Directory => Path.GetFileName(Directory)).ToArray();
    Array.Sort(arrProjectList);

    int iProjectCount = arrProjectList.Length;
    this.DropDownListSize = iProjectCount;

    for (int i = 0; i < iProjectCount; i++)
    {
        SelectJobDropdown.Items.Add(arrProjectList[i]);
    }
}

This gives me a nice drop down list of all current directories.

Now, I need to add a filer to show only items which contain a text typed into the ComboBoxitself regardless if the dropdown list itself is open or not.

I have disabled both AutoCompleteMode and AutoCompleteSource as it was not working as expected with the opened droped down list. It was opening additonal list on top the existing one but I could only select from the dropdown under it. See print screen below: 

The list on top is inactive and I cannot select the text but also does not give an option to display substrings.

Only have one even for the box itself which is

private void SelectJobDropdown_SelectedIndexChanged(object sender, EventArgs e) 
{
    //Plenty of code here 
}

Can someone point in the right direction how to filter the list as I type within the box itself.

Please NOTE I have been using C# for only 3 weeks so might get confused with some of the terminology or other aspects of this language etc.

This is not a duplicate. I have different requirements sadly. I do not want to display a new list as I type along. I want to filter the main list as type. This solution must search for any text within combobox item and not just the starting characters. The solution you have suggested does include additional list and does not allow for searching any string within an item. So for example if item contains 99999 Hello World, I want to be able to display matching line by typing any of the words in the item and not just 99999. Hope this makes sense. – TomTommyTom Dec 6 '16 at 7:55 

  • I'm starting to think I will need to add a new event(textChanged or TextAppend) and and search the array for any matches and rebuild the list each time but not too sure if this is the best solution. Cheers – TomTommyTom Dec 6 '16 at 7:57

  • I would not rebuild the list each time. I would extract the values based on the filter and bind it to the list. I am working right now on a solution, but have not yet figured it out. My problem is to write into the ComboBox. – Mong Zhu Dec 6 '16 at 8:13

 

I would suggest to use 2 Lists. 1 for the original values

List<string> arrProjectList;

public ReconciliationReport()
{
    InitializeComponent();
    AppDomain.CurrentDomain.AssemblyResolve += FindDLL;

    this.sRootDirectory = Properties.Resources.sRootDirectory;

    arrProjectList = Directory.GetDirectories(sRootDirectory).Select(Directory => Path.GetFileName(Directory)).ToList();
    arrProjectList.Sort();

    // then just bind it to the DataSource of the ComboBox
    SelectJobDropdown.DataSource = arrProjectList;
    // don't select automatically the first item
    SelectJobDropdown.SelectedIndex = -1;
}

and 1 for the filtered values.

In this example I use a TextBox to catch the filter text.

In the TextChanged event take the filter text and pull out only those values from the original arrProjectList List.

You would need an extra option at the end to reset the binding to the old list if the filter is empty.

private void textBox1_TextChanged(object sender, EventArgs e){
    string filter_param = textBox1.Text;

    List<string> filteredItems = arrProjectList.FindAll(x => x.Contains(filter_param));
    // another variant for filtering using StartsWith:
    // List<string> filteredItems = arrProjectList.FindAll(x => x.StartsWith(filter_param));

    comboBox1.DataSource = filteredItems;

    // if all values removed, bind the original full list again
    if (String.IsNullOrWhiteSpace(textBox1.Text))
    {
        comboBox1.DataSource = arrProjectList;
    }

    // this line will make sure, that the ComboBox opens itself to show the filtered results       
}

EDIT

I found a Solution for typing the filter into the ComboBox directly. The filtering is the same procedure, but using the TextUpdate event it is necessary to unselect the SelectedIndex which is automatically set to the first element after the binding. Then I guess you want to proceed to write your filter (more than just one letter), write the filter back into the ComboBox.Text property and set the cursor position to the end:

private void comboBox1_TextUpdate(object sender, EventArgs e) {
    string filter_param = comboBox1.Text;

    List<string> filteredItems = arrProjectList.FindAll(x => x.Contains(filter_param));
    // another variant for filtering using StartsWith:
    // List<string> filteredItems = arrProjectList.FindAll(x => x.StartsWith(filter_param));

    comboBox1.DataSource = filteredItems;

    if (String.IsNullOrWhiteSpace(filter_param))
    {
        comboBox1.DataSource = arrProjectList;
    }
    comboBox1.DroppedDown = true;
    Cursor.Current = Cursors.Default;

    // this will ensure that the drop down is as long as the list
    comboBox1.IntegralHeight = true;

    // remove automatically selected first item
    comboBox1.SelectedIndex = -1;

    comboBox1.Text = filter_param;

    // set the position of the cursor
    comboBox1.SelectionStart = filter_param.Length;
    comboBox1.SelectionLength = 0;            
}

Et voilà automatic filtering with a nice display and arrow selection afterwards.

EDIT 2

for case insensitive search you can use this:

List<string> filteredItems = arrProjectList.FindAll(x => x.ToLower().Contains(filter_param.ToLower()));

NOTE:

After the opening of the dropdownlist the cursor will disappear. To prevent this the Cursor.Currenthas to be set to Cursor.Defualt

comboBox1.DroppedDown = true;
Cursor.Current = Cursors.Default;

 

18.7k77 gold badges3030 silver badges6060 bronze badges

  • Cool Thanks. I will give that a go and report back. – TomTommyTom Dec 6 '16 at 8:51

  • Great. Just reading through it. Cheers Will let you know later. – TomTommyTom Dec 6 '16 at 8:54

  • @TomPisz a simple trick should do it: reset the IntegralHeight property and put it in front of the 

    • DroppedDown = true line: 
      comboBox1.IntegralHeight = false; 
      comboBox1.IntegralHeight = true; 
      comboBox1.DroppedDown = true;

       

  • amazing! this is pretty relevant and the closest match to an answer I found yet! Good work! – sksallaj Jul 10 '19 at 4:01

  • That was the first thing I did, I'm a good stackoverflow user :-D – sksallaj Jul 10 '19 at 12:23

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值