Custom on-screen keyboard and filtered dropdown not playing nicely

196 views Asked by At

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

1

There are 1 answers

7
mjpolak On BEST ANSWER

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.