0

I have a custom on-screen ten key pad in my winforms app that seems to work just fine everywhere except a single dropdown that has some custom filtering code. I've gone out of my way to make this keypad noninteractive in every way I can imagine. Here's the code-behind (numpad0, numpad1, etc are all labels if that matters)

public partial class TenKeyForm : Form
{
    private const int WM_NCLBUTTONDOWN = 0xA1;
    private const int HT_CAPTION = 0x2;
    private const int WM_MOUSEACTIVATE = 0x0021, MA_NOACTIVATE = 0x0003;
    private const int WS_EX_NOACTIVATE = 0x08000000;

    protected override bool ShowWithoutActivation { get { return true; } }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MOUSEACTIVATE)
        {
            m.Result = (IntPtr)MA_NOACTIVATE;
            return;
        }
        base.WndProc(ref m);
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams createParams = base.CreateParams;
            createParams.ExStyle |= WS_EX_NOACTIVATE;
            return createParams;
        }
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern bool ReleaseCapture();

    public TenKeyForm()
    {
        InitializeComponent();
        MouseDown += MouseDownHandler;

        numpad0.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("0"); });
        numpad1.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("1"); });
        numpad2.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("2"); });
        numpad3.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("3"); });
        numpad4.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("4"); });
        numpad5.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("5"); });
        numpad6.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("6"); });
        numpad7.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("7"); });
        numpad8.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("8"); });
        numpad9.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("9"); });
        numpadDot.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("."); });
        numpadBack.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("+{TAB}"); });
        numpadForward.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("{TAB}"); });
        numpadBackspace.MouseUp += new MouseEventHandler(delegate (object o, MouseEventArgs e) { SendKeys.Send("{BS}"); });

        SetStyle(ControlStyles.Selectable, false);
    }

    private void MouseDownHandler(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            ReleaseCapture();
            SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
        }
    }
}

Again, this works just fine for the various textboxes, masked inputs, etc except for a single ComboBox. This ComboBox has some extra validation on it to prevent invalid selections from being typed. Basically, if it has an entry "500" and "550" typing "5" jumps to "500" in the list, and leaves everything but the "5" you typed highlighted so the next keystroke will replace it, i.e. 500. It will reject anything but "5" or "0" at this point. Typing another 5 highlights 550 and so on. Here is the codebehind for that bit:

    private void ValidateDropDown(object sender, KeyPressEventArgs e)
    {
        ToolStripComboBox tcb = (ToolStripComboBox)sender;
        ComboBox cb = tcb.ComboBox;
        cb.DroppedDown = true;
        string strFindStr = "";
        if (e.KeyChar == (char)8)
        {
            if (cb.SelectionStart <= 1)
            {
                cb.Text = "";
                return;
            }

            if (cb.SelectionLength == 0)
                strFindStr = cb.Text.Substring(0, cb.Text.Length - 1);
            else
                strFindStr = cb.Text.Substring(0, cb.SelectionStart - 1);
        }
        else
        {
            if (cb.SelectionLength == 0)
                strFindStr = cb.Text + e.KeyChar;
            else
                strFindStr = cb.Text.Substring(0, cb.SelectionStart) + e.KeyChar;
        }
        int intIdx = -1;
        // Search the string in the ComboBox list.
        intIdx = cb.FindString(strFindStr);
        if (intIdx != -1)
        {
            cb.SelectedText = "";
            cb.SelectedIndex = intIdx;
            cb.SelectionStart = strFindStr.Length;
            cb.SelectionLength = cb.Text.Length;
            e.Handled = true;
        }
        else
            e.Handled = true;
    }

Now the ten-key works fine everywhere but this ComboBox, and this ComboBox works perfectly if you type with an actual keyboard. The on-screen keyboard even works correctly for the first keypress, but any subsequent mousedown event on the tenkey form seems to perform something like SelectAll() on the ComboBox, thus negating the carefully set selection. Typing "55" in the above example, does not navigate to 550 but instead navigates to 500 twice, replacing the entire text with each click.

What is causing this odd behavior and how can I stop it?

Update: It appears to be a problem specific to ToolStrip ComboBoxes, should have thought to include that here. Also: AutoCompleteMode = AutoCompleteMode.SuggestAppend

David Perry
  • 1,324
  • 5
  • 14
  • 31

1 Answers1

1

How do you set AutoCompleteMode?

I made test, and setting it:

AutoCompleteMode = AutoCompleteMode.Suggest;

solved described issue.

EDIT 1

I have found a workaround for that issue, you can add:

toolStripComboBox1.SelectionLength=0;

before SendKey function.

mjpolak
  • 721
  • 6
  • 24
  • 1
    SuggestAppend is my current setting. Setting it to Suggest does not change the behavior at all. Does it matter that it's a toolstrip combobox? – David Perry Jan 03 '17 at 19:13
  • 1
    That maybe a case! I added combobox in toolstrip and same behavior occurs. Looking for some workaround. – mjpolak Jan 03 '17 at 19:25
  • Well at least I'm not crazy. Nothing more unsatisfying than "could not reproduce." – David Perry Jan 03 '17 at 19:27
  • Hmm, that workaround doesn't seem to be working on my end. Is your tenkey form an MDI Child of the form with the toolstripcombobox? – David Perry Jan 03 '17 at 21:54
  • Can you add toolStripComboBox1.Focus(); before SelectionLength=0; ? – mjpolak Jan 04 '17 at 07:17
  • I could, but then that would break the tenkey for every form element BUT the combobox. :( – David Perry Jan 04 '17 at 16:25
  • 1
    I think I'm just gonna re-work my UI so I can use a regular combobox, at which point your answer works. Obviously something about the ToolStripComboBox control is weird/broken so I'm just not gonna use it. – David Perry Jan 04 '17 at 16:32