0

I'm adding hotkey support to an app, and throughout, I use CTRL+ENTER as the hotkey for confirming dialogs.

I'm having a unique issue in the dialog below, which contains a ListBox. If the ListBox has the focus (it usually does) when the CTRL+ENTER hotkey is pressed, it intermittently shifts the ListBox selection down one position.

This creates a problem, since changing the selected item in the ListBox updates the New Customer Name field. What's happening is users choose the settings they want, hit CTRL+ENTER to execute the merge, and the settings suddenly change just before the merge occurs.

I'm not handling any key events on the ListBox, and the selection change is weirdly intermittent (perhaps 1 in 8 times). I also can't seem to get ENTER or CTRL+ENTER to cause selection changes intentionally.

What causes this behavior, and how do I suppress it?

enter image description here

Hotkey Handling

The approach I'm using to Form-level hotkey handling is to set Form.KeyPreview = true and to handle the KeyUp event. Based on this SO post, this is the cleanest way to support the handling of modifier keys, and it seems to work like a charm.

    private void frmMerge_KeyUp(object sender, KeyEventArgs e)
    {
        switch (e.KeyCode)
        {
            case Keys.Escape:    

                Close(); // Close Form 

                return;
            case Keys.Enter:

                if (e.Control) // CTRL+ENTER  
                    ActionMerge(); // Merge Customer 

                return;
        }
    }

ListBox Use

The ListBox is setup very simply. It is populated once on Form Load;

    private void frmMerge_Load(object sender, EventArgs e)
    {

        // Add all Customers to the list 
        foreach(Customer customer in Customers)
        {
            lbxNames.Items.Add(customer.Name); 
        }

        // If there are items, select the first one 
        if (lbxNames.Items.Count > 0)
            lbxNames.SelectedIndex = 0; 

    }

And it handles one event, SelectedIndexChanged, to update the New Customer Name TextBox

    private void lbxNames_SelectedIndexChanged(object sender, EventArgs e)
    {
        txtName.Text = lbxNames.SelectedItem as string; 
    }

These are the only two places that the ListBox is touched in code.

Memetican
  • 381
  • 5
  • 18
  • could you please post some code to make your problem reproducible ? – Mong Zhu Jul 28 '17 at 08:30
  • Thanks @Mong, done. There isn't any other code that relates to key handling, or ListBox events. – Memetican Jul 28 '17 at 08:41
  • what happens in `ActionMerge` ? any access to the listbox? – Mong Zhu Jul 28 '17 at 09:11
  • @MongZhu No, none at all. The list box is set up very simply. It's populated on form load, and it has a `SelectedIndexChanged` event handler which updates the New Customer Name textbox.. I've added that code to my question. – Memetican Jul 28 '17 at 09:24
  • sorry dude, unable to reproduce your problem. – Mong Zhu Jul 28 '17 at 10:35
  • Thanks @MongZhu, I'm continuing to explore the problem. I've discovered that the intermittency is actually connected to using the CTRL+M hotkey to open the Merge dialog. For some reason that 'M' seems to stick in a key queue somewhere, perhaps in the keyboard buffer or driver? On executing the CTRL+ENTER hotkey, that M fires as well. In the ListBox, pressing M only shifts the selected record if there is a record beginning with 'M'. – Memetican Jul 29 '17 at 23:38

1 Answers1

0

I've found a workaround to the problem, but not determined the cause. My best guess is that I'm dealing with a keyboard hardware or driver issue, rather than a ListBox issue.

The intermittent nature of the problem was caused by the CTRL+M hotkey combination, which is used to open my Merge dialog. That M appears to stick in a key queue somewhere, perhaps in the hardware keyboard buffer or driver. On executing the CTRL+ENTER hotkey, that M fires a second time. In the ListBox, pressing M only shifts the selected record if there is a record beginning with M.

Workaround

I made two changes. First I suppress the M key in the ListBox. This works fine for me, since I only need arrow key and home/end navigation in the ListBox, and not alphabetic selection.

    private void lbxNames_KeyDown(object sender, KeyEventArgs e)
    {
        // Suppress listbox hotkeys to prevent dialog hotkey conflicts
        switch (e.KeyCode)
        {
            case Keys.M:
                e.Handled = true;
                e.SuppressKeyPress = true; // required
                break;
        }
    }

Second, I changed my dialog-level hotkeys handler to use the ProcessCmdKey approach in place of KeyUp. The reason for this is that using KeyUp event handlers conflicted strangely between the Form level and the ListBox level. Many keys would get captured by the ListBox first, even with KeyPreview enabled.

    protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, Keys keyData)
    {

        switch (keyData)
        {
            case Keys.Escape:
                // Close the dialog
                Close();
                return true;
            case Keys.Control | Keys.Enter:
                // Perform the primary action
                ActionMerge();
                return true;
        }

        return base.ProcessCmdKey(ref msg, keyData);
    }
Memetican
  • 381
  • 5
  • 18