5

I have a ComboBox that is part of a detail display related to a data grid containing rows from a database. No binding to the ComboBox exists, I am doing this manually. The ComboBox allows manual entry, as if it were a text field, while still providing a drop-down of choices.

My issue is that if I have manually entered text in the field, and the drop-down is clicked, the ComboBox apparently wants to seek out a match. Also, it appears that the search is simple, so KG matches KG/Day. I must avoid this and force an exact match.

But further, I think I need to be able to govern the entire process myself, because to further complicate the matter, the drop-down item would actually read KG/Day - kilograms/day. The database field from which the data is fetched, however, is only storing the portion prior to the hyphen, so KG/Day.

So, I need to intercept the drop-down action in a way that allows me to do two things:

1) Perform my own search to find whether or not I have ad-hoc text, or a "real" match. As in that it was originally selected from the drop-down; in other words, that I have KG/Day and not just KG.

2) Eliminate the auto-search behavior that ComboBox wants to do.

I have tried getting in front of these things using method handlers in the Form, such as

ComboBox::DropDown() and ComboBox::DropDownClosed(),

but it seems these still don't allow me to stop the basic ComboBox searching/matching.

I have also tried creating a class of my own inherited from ComboBox, but I don't really know what to override, or in general how to go about getting what I want, stopping what I don't.

So, with that, I thank you for your advice.

EDIT: to expand on what I tried already... In my inherited class, I was attempting to use a WndProc override. Based on some advice I found in another forum, my goal was to intercept the ComboBox message LB_FINDSTRING and replace it with LB_FINDSTRINGEXACT. The post suggested that ComboBox defaulted to LB_FiNDSTRING, which fits what I see it doing, and that subbing LB_FINDSTRINGEXACT would cure the problem. Trouble is, unless I got a bad definition for LB_FINDSTRING, it was never received.

Here's my enum:

[Flags]
public enum ListBoxFlags
{
    LB_ADDSTRING = 0x0180,
    LB_SETSEL = 0x0185,
    LB_GETSELITEMS = 0x0191,
    LB_GETSELCOUNT = 0x0190,
    LB_GETCURSEL = 0x0188,
    LB_SELECTSTRING = 0x018C,
    LB_SETCURSEL = 0x0186,
    LB_FINDSTRING = 0x018F,
    LB_FINDSTRINGEXACT = 0x01A2,
    LB_GETCOUNT = 0x018B,
    LB_GETSEL = 0x0187,
    LB_GETTEXT = 0x0189,
    LB_RESETCONTENT = 0x0184,
    LB_SETHORIZONTALEXTENT = 0x0194,
    LB_GETHORIZONTALEXTENT = 0x0193,
    LB_GETTOPINDEX = 0x018E,
    LB_SETTOPINDEX = 0x0197,
    LB_INSERTSTRING = 0x0181,
    LB_DELETESTRING = 0x0182,
    LB_GETITEMDATA = 0x0199
}
DonBoitnott
  • 10,787
  • 6
  • 49
  • 68
  • What if you changed the `DropDownStyle` to `DropDownList`? This will prevent entry of items that are not in the list, however it will still match the items when you type text. The difference is that the text in the combobox is always either empty or an exact match. – John Willemse May 14 '13 at 13:40
  • @JohnWillemse - because such items are permissible in the data. Think of the list as suggestions, not absolute requirements. – DonBoitnott May 14 '13 at 13:47
  • 1
    You were on the right track. A combobox consists of three handles, you have to explicitly get the handle to the listbox window. See: http://stackoverflow.com/questions/25681886/prevent-autoselect-behavior-of-a-system-window-forms-combobox-c – Loathing Sep 06 '14 at 02:27
  • Upvote for Loathing's comment back to the other stack link. It has the correct answer the original poster was looking for. No funky workarounds required. – Frog Pr1nce Nov 23 '15 at 16:33

1 Answers1

0

Made some sample code that might help - you can use as a guide.

The idea is to handle the TextChanged event of the ComboBox, and really just modify the ComboBox list items at that point. The example below will modify the list to add the current text (most important, as this will not change the text when you click the combobox) and any other items that meet the search criteria.

I don't think you need the code to re-initialize the list items when focus is lost, but left in there just in case.

    //contains a list of default items for the combobox items
    List<string> comboList = new List<string>();

    public Form1()
    {
        InitializeComponent();
        initComboList(); //initialize the defaults
        initCombobox(); //initialize the combobox list items
    }

    //fills the defaults for the combobox items
    private void initComboList()
    {
        comboList.Add("red");
        comboList.Add("blue");
        comboList.Add("green");
    }

    //initializes the combobox items
    private void initCombobox()
    {
        comboBox1.Items.Clear();
        foreach (string s in comboList)
            comboBox1.Items.Add(s);
    }

    //occurs when the text changes in the combobox
    private void comboBox1_TextChanged(object sender, EventArgs e)
    {
        string curtext = comboBox1.Text;
        insertIntoComboBox(curtext);   //insert the current text into combobox
        comboBox1.Select(curtext.Length, 0); //if you don't do this, the cursor goes back to index 0 :-(
    }

    //called whenever is desired to insert the current text into the combobox items
    private void insertIntoComboBox(string curtext)
    {
        comboBox1.Items.Clear();
        //only add the current text if it's not already in the list of defaults and not empty string
        if (comboList.Contains(curtext) == false && curtext.Length > 0)
            comboBox1.Items.Add(curtext);
        foreach (string s in comboList)
                comboBox1.Items.Add(s);
    }

    //called whenever combobox loses focus
    private void comboBox1_Leave(object sender, EventArgs e)
    {
        initCombobox();
    }
Mash
  • 1,496
  • 13
  • 17
  • I appreciate the suggestion, but I would really prefer to prevent the matching altogether, thus leaving my list intact and unmodified. My users expect a consistent list of choices, as it is taken from a government standard list. Unfortunately, this method would make for a constantly changing and unpredictable list. – DonBoitnott May 14 '13 at 15:17
  • OK, understood. Based on your comment reply to John Willemse, the drop down list items are not the only items that are valid, correct? Is there any reason your are not filling the combobox with the list of ALL items that are valid? – Mash May 14 '13 at 16:34
  • The list consists of stock choices provided by the government. Users can use their own text, however. I cannot pre-populate simply because I can't predict all of the possibilities. There are likely hundreds, maybe over a 1000 possibilities/variations. Therefore, I need a simplistic approach: provide what stock options I know of, and allow entry of all else. The drop-down behavior is bad because it changes the selected value automatically. If I type "KG" (which is not stock), then opening the drop-down is enough to change my selection...I don't actually have to click a choice. – DonBoitnott May 14 '13 at 16:43
  • hmm, I see. Interesting problem. Is it acceptable to insert a new item (temporarily) into `Combobox.Items` every time the text has changed? You would just have to make sure it wasn't in the list already. This way, when the user types something and clicks the ComboBox, it would select the temp item instead of trying to find another match. This temp item would get replaced every time the text changes, so it will not persist. I've updated the code I posted to do this instead. – Mash May 14 '13 at 17:09
  • In the end, I implemented your solution. But I have to be honest, it feels more like a work around rather than really having gotten to the solution I was asking for. Regardless, it's seems to be working, and I thank you. – DonBoitnott May 15 '13 at 11:35
  • You're right, it is a work-around. I understand what you were really looking for, and if I figure anything out I'll let you know. – Mash May 15 '13 at 13:27