winformsvisual-c++autocompletetoolstripcombobox

Customized Autosearch of ToolStripCombobox in Visual C++


I want to implement a toolstripcombobox that that acts like autocompletemode is set to suggest. I didn't set the autocomplete mode since it only finds prefix identical items.

What I want is that it can also find items in the combobox that has a substring even if it doesn't start with that.

sample list:

January, February, March, April, May, June, July, August, September, October, November, December

If I type in the toolstripcombobox for "ber", it should display in the dropdown:

September
October
November
December

respectively.

As of now, I created a separate list that contains the items:

void populateList()
{
  this->storageList = gcnew Generic::List<String ^>;
  storageList->Add("January");
  storageList->Add("February");
  storageList->Add("March");
  storageList->Add("April");
  storageList->Add("May");
  storageList->Add("June");
  storageList->Add("July");
  storageList->Add("August");
  storageList->Add("September");
  storageList->Add("October");
  storageList->Add("November");
  storageList->Add("December");
}

and I added a TextUpdate event for the ToolStripCombobox:

      void handleTextChange()
      {
        String ^ searchText = toolStripComboBox->Text;
        toolStripComboBox->Items->Clear();
        Cursor->Current = Cursors::Default;

        if(searchText != "")
        {
          toolStripComboBox->DroppedDown = true;
          Regex ^ searchRegex = gcnew Regex("(?i).*"+searchText+".*");
          for(int i = 0; i<storageList->Count; i++)
          {
            Match ^ m = searchRegex->Match(storageList[i]);
            if(m->Success)
            {
              toolStripComboBox->Items->Add(storageList[i]);
            }
          }

          if(toolStripComboBox->Items->Count > 0)
          {
            String ^ sText = toolStripComboBox->Items[0]->ToString();
            toolStripComboBox->SelectionStart = searchText->Length;
            toolStripComboBox->SelectionLength = sText->Length - searchText->Length;

          }
          else
          {
            toolStripComboBox->DroppedDown = false;
            toolStripComboBox->SelectionStart = searchText->Length;
          }
        }
        else
        {
          toolStripComboBox->DroppedDown = false;
          toolStripComboBox->Items->Clear();
        }
      }

This is my sample implementation. It already searches non-prefix but I'm not quite satisfied with the code since there exists some differences when autocompletemode in suggest is set:

1) When you keypress up or down the drop down for the items, the selectedIndexChanged Event fires unlike the autocompletemode that doesn't
2) And many more minor differences.

What I really want is that It will just imitate the autocomplete mode in suggest but it will search non-prefix-cally..

Any sample codes, links, or suggestions are well appreciated. :)


Solution

  • With the help of this indirect but useful samples, I was finally able to solve this problem.

    Override Winforms ComboBox Autocomplete Suggest Rule
    http://www.codeproject.com/Articles/3958/A-C-auto-complete-combo-box
    http://www.codeproject.com/Tips/631196/ComboBox-with-Suggest-Ability-based-on-Substring-S

    and with the problem of selectedIndexChanged was solved by this:
    Stop comboBox's selectedIndexChanged event from firing when the form loads and
    The selectedIndexChanged is auto triggered without selecting items in combobox in c# windows application

    To sum it all up, in order to create an imitation of autocompletemode in suggest that dont just base on prefixed searches, you need to subscribe to several events of ToolStripComboBox and ComboBox.

    Here are the events that you need to create and modify:

    toolStripComboBox_TextUpdate
    toolStripComboBox_KeyDown
    toolStripComboBox_DropDown
    toolStripComboBox_ChangeCommit
    


    On TextUpdate():

    toolStripComboBox_TextUpdate(System::Object^  sender, System::EventArgs^  e) {
      String ^ searchText = toolStripComboBox->Text;
      toolStripComboBox->Items->Clear();
    
      if(searchText != "") {
        Regex ^ searchRegex = gcnew Regex("(?i).*"+searchText+".*");
    
        for(int i = 0; i<storageList->Count; i++) {
          Match ^ m = searchRegex->Match(storageList[i]);
          if(m->Value == storageList[i]) {
            toolStripComboBox->Items->Add(storageList[i]);
          }
        }
    
        if(toolStripComboBox->Items->Count > 0) {
          toolStripComboBox->DroppedDown = true;
          toolStripComboBox->Text = searchText;
          Cursor->Current = Cursors::Default;
        }
        else {
          toolStripComboBox->DroppedDown = false;
        }
        toolStripComboBox->SelectionStart = searchText->Length;  
      }
      else {
        toolStripComboBox->DroppedDown = false;
        toolStripComboBox->Items->Clear();
      }
    }
    

    TextUpdate summary: this event just handles the matching and population of the toolStripComboBox and the status of the drop down


    On KeyDown():

    toolStripComboBox_KeyDown(System::Object^  sender, System::Windows::Forms::KeyEventArgs^  e) {
      String ^ searchText = toolStripComboBox->Text;
      if(e->KeyCode == Keys::Down || e->KeyCode == Keys::Up) {
        if(e->KeyCode == Keys::Down) {
          if(toolStripComboBox->SelectedIndex == -1 && toolStripComboBox->Items->Count > 0) {
                 toolStripComboBox->SelectedIndex = 0;
          }
        }
        if(e->KeyCode == Keys::Up) {
          if(toolStripComboBox->SelectedIndex == -1 && toolStripComboBox->Items->Count > 0) {
            toolStripComboBox->SelectedIndex = toolStripComboBox->Items->Count - 1;
          }
        }
        keydownTxt->Text = Convert::ToString(toolStripComboBox->SelectedIndex);
      }
      if(e->KeyCode == Keys::Back) {
        toolStripComboBox->SelectionStart = searchText->Length;
      }
      if(e->KeyCode == Keys::Enter) {
        toolStripComboBox_ChangeCommit(sender, e);
      }
    }
    

    KeyDown Summary: this handles the special keys that are pressed like the up and down arrow, backspace, and the enter key.. notice that the event ChangeCommit() was triggered when you press enter. this happens because ChangeCommit event does not fire when you press enter, only by mouse click.


    On DropDown():

    toolStripComboBox_DropDown(System::Object^  sender, System::EventArgs^  e) {
      String ^ searchText = toolStripComboBox->Text;
      toolStripComboBox->SelectionStart = searchText->Length;
    }
    

    DropDown Summary: its just a minor fix since when the DroppedDown property is set to true, it prevents the editable part of the ToolStripComboBox to select the first item on the list.


    On ChangeCommit(): Since I have a problem like this:
    1) When you keypress up or down the drop down for the items, the selectedIndexChanged Event fires unlike the autocompletemode that doesn't
    The solution to this is to unsubscribe from the SelectedIndexChanged and replace it with an event handler ChangeCommit which is not a method in the ToolStripComboBox but is a method of the ComboBox:

    this->toolStripComboBox->ComboBox->SelectionChangeCommitted += gcnew System::EventHandler(this, &Form1::toolStripComboBox_ChangeCommit);

    After this murderous implementation, YEEEY! I've successfully imitated the autocompletemode suggest with suggesting items matching only a substring in the text..!!
    This problem can also be a solution for Simple ComboBoxes.
    This method may be a little bit dirty, so others can just inherit and override the events so it can make the code neater.