0

I want to stop any trace of autocomplete from happening with my ComboBox.

Is it even possible to stop the Windows Form ComboBox object from auto-completing? Right now when I type the first three characters of something and click another ComboBox to type in the new field, the first ComboBox does not change and keeps those three typed letters. However, if I resize the form then it tries to autocomplete the ComboBox (if there is a matching record that STARTS with the text I typed). Also, the ComboBox's this happens to are Anchored: Left, Right. I have a bad feeling the only way to stop this behavior is to subclass the ComboBox and do something in the resize event or whatever... :/

Here are the settings for what I think are the only properties dealing with autocomplete... enter image description here

Note: The "Collection" in the AutoCompleteCustomSource has an empty list when I click the three dots to the right.

To Reproduce This Issue:

  1. Create a form and place a standard ComboBox control on it. Then place another one right below it. You will do nothing to the second ComboBox (only used to have a second control to tab to in the example).

  2. In the ComboBox properties find the Items collection and add the following...

  • testing123
  • blah321
  • foobar
  1. In the ComboBox properties find the Anchor property and change to Left, Right.

  2. Run the application.

  3. Test 1: In the ComboBox type bl and press the tab key to get to the next ComboBox. You will see no change.

  4. Test 2: In the ComboBox type bl and resize the form by dragging the right side to make it a little wider. You will see the ComboBox has now been auto filled with blah321.

Arvo Bowen
  • 4,524
  • 6
  • 51
  • 109

1 Answers1

0

Found what I was looking for over at https://stackoverflow.com/a/25696213/1039753!

I guess there is no way to stop it without subclassing or making an extension class/method to do it. What I was looking for was "auto complete" when I guess I should have been looking for "auto select". Knowing what to search for is key! This code works great, to use it you would do something like...

ComboBoxAutoSelectEx.AutoSelectOff(ComboBox1);

I could not see any performance hit either. It just looks like magic. I have slightly altered the code from that answer to add the required structure needed.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;

using Opulos.Core.Win32;

namespace MyAppsNamespace {

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT {
      public int Left;
      public int Top;
      public int Right;
      public int Bottom;
    }

    // Extension class to disable the auto-select behavior when a combobox is in DropDown mode.
    public static class ComboBoxAutoSelectEx {
    
        public static void AutoSelectOff(this ComboBox combo) {
            Data.Register(combo);
        }
    
        public static void AutoSelectOn(this ComboBox combo) {
            Data data = null;
            if (Data.dict.TryGetValue(combo, out data)) {
                data.Dispose();
                Data.dict.Remove(combo);
            }
        }
    
        private class Data {
            // keep a reference to the native windows so they don't get disposed
            internal static Dictionary<ComboBox, Data> dict = new Dictionary<ComboBox, Data>();
    
            // a ComboBox consists of 3 windows (combobox handle, text edit handle and dropdown list handle)
            ComboBox combo;
            NW nwList = null; // handle to the combobox's dropdown list
            NW2 nwEdit = null; // handle to the edit window
    
            internal void Dispose() {
                dict.Remove(this.combo);
                this.nwList.ReleaseHandle();
                this.nwEdit.ReleaseHandle();
            }
    
            public static void Register(ComboBox combo) {
                if (dict.ContainsKey(combo))
                    return; // already registered
    
                Data data = new Data() { combo = combo };
                Action assign = () => {
                    if (dict.ContainsKey(combo))
                        return; // already assigned
    
                    COMBOBOXINFO info = COMBOBOXINFO.GetInfo(combo); // new COMBOBOXINFO();
                    //info.cbSize = Marshal.SizeOf(info);
                    //COMBOBOXINFO2.SendMessageCb(combo.Handle, 0x164, IntPtr.Zero, out info);
    
                    dict[combo] = data;
                    data.nwList = new NW(combo, info.hwndList);
                    data.nwEdit = new NW2(info.hwndEdit);
                };
    
                if (!combo.IsHandleCreated)
                    combo.HandleCreated += delegate { assign(); };
                else
                    assign();
    
                combo.HandleDestroyed += delegate {
                    data.Dispose();
                };
            }
        }
    
        private class NW : NativeWindow {
            ComboBox combo;
            public NW(ComboBox combo, IntPtr handle) {
                this.combo = combo;
                AssignHandle(handle);
            }
    
            private const int LB_FINDSTRING = 0x018F;
            private const int LB_FINDSTRINGEXACT = 0x01A2;
    
            protected override void WndProc(ref Message m) {
                if (m.Msg == LB_FINDSTRING) {
                    m.Msg = LB_FINDSTRINGEXACT;
                }
    
                base.WndProc(ref m);
    
                if (m.Msg == LB_FINDSTRINGEXACT) {
                    String find = Marshal.PtrToStringAuto(m.LParam);
                    for (int i = 0; i < combo.Items.Count; i++) {
                        Object item = combo.Items[i];
                        if (item.Equals(find)) {
                            m.Result = new IntPtr(i);
                            break;
                        }
                    }
                }
            }
        }
    
        private class NW2 : NativeWindow {
            public NW2(IntPtr handle) {
                AssignHandle(handle);
            }
    
            private const int EM_SETSEL = 0x00B1;
            private const int EM_GETSEL = 0x00B0;
    
            protected override void WndProc(ref Message m) {
                if (m.Msg == EM_SETSEL) {
                    // if this code is not here, then the entire combobox text is selected
                    // which looks ugly, especially when there are multiple combo boxes.
                    //
                    // if this method returns immediately, then the caret position is set
                    // to (0, 0). However, it seems that calling EM_GETSEL has a side effect
                    // that the caret position is mostly maintained. Sometimes it slips back
                    // to (0, 0).
                    SendMessage(Handle, EM_GETSEL, IntPtr.Zero, IntPtr.Zero);
                    //int selStart = (sel & 0x00ff);
                    //int selEnd = (sel >> 16) & 0x00ff;
                    //Debug.WriteLine("EM_GETSEL: " + selStart + " nEnd: " + selEnd);
                    return;
                }
                base.WndProc(ref m);
            }
    
            [DllImportAttribute("user32.dll", SetLastError=true)]
            private static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
        }
    
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct COMBOBOXINFO {
        public Int32 cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public int buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    
        public static COMBOBOXINFO GetInfo(ComboBox combo) {
            COMBOBOXINFO info = new COMBOBOXINFO();
            info.cbSize = Marshal.SizeOf(info);
            SendMessageCb(combo.Handle, 0x164, IntPtr.Zero, out info);
            return info;
        }
    
        [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
        private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);
    }
}
Arvo Bowen
  • 4,524
  • 6
  • 51
  • 109