简体   繁体   中英

KeyDown event not firing?

When coding a small game, I encountered a problem; my form's KeyDown and KeyUp events don't fire at all.

This is the form's code:

public class GameForm : Form
{
    private ControllableBlock player;

    public GameForm()
    {
        KeyDown += Game_KeyDown;
        KeyUp += Game_KeyUp;

        player = new ControllableBlock();
        Controls.Add(player);
    }

    private void Game_KeyDown(object sender, KeyEventArgs e)
    {
        player.ReactToKey(e.KeyCode);
    }

    private void Game_KeyUp(object sender, KeyEventArgs e)
    {
        player.ReactToKey(e.KeyCode);
    }
}

There's a lot more going on, but I only pasted the relevant code.

I've already tried setting this.KeyPreview = true; and calling this.Focus(); , neither works. The problem is not in ReactToKey() method, I've already set a breakpoint there and the event is never fired.


Edit: After some tests I've come to a conclusion that the problem is within my ControllableBlock . Yet, I have no idea why, but I'm working on it. If I comment out everything that's related to the player , the events start firing.


Edit 2: Seems like the problem is me inheriting my ControllableBlock from Control . If I inherit it from Panel , it works fine. Why is this? Can't I fire an event if I inherit from control? The ControllableBlock class is empty for now, so it doesn't even do anything other than inherits from Control .


Edit 3: Now that I've started a bounty, I'd like to clarify that I'm not looking for a solution on how to make the events fire, I'm looking for a reason on why they don't fire if I inherit from Control .

If your events should be application-wide try to set property KeyPreview to true - it will allow you to fire respective events regardless of focused control.

this.KeyPreview = true;

Otherwise you should attach these events directly to control that will process them.

Edit:

I removed InitializeComponent(); from my form and got behaviour identical to yours.

After implementing solution provided in this question all events started to qork perfectly.
Copy code snippet here:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
        if (keyData == Keys.Left) {
            // Do your staff for Left Key here

            return true;
        }
        // you need to add if condition to every direction you want to handle
        return base.ProcessCmdKey(ref msg, keyData);
    }

I was able to reproduce a similar issue (which is hopefully related..)

Explanation:

  • Controls which return CanSelect==true are selectable for keyboard input
  • A blank descendent of Control() is selectable, one of Panel() is not
  • The first selectable control added to a form will get selected
  • A selected control will steal keyboard events from its parents by default
  • Certain keys used for navigation within a window require extra steps to be handleable

Check here for a good overview of how windows keyboard input works.

Code to reproduce it:

public class GameForm : Form
{
    public GameForm()
    {
        this.KeyDown += Game_KeyDown;
        var tests = new List<Control[]>() { 
            new[] { new Panel() },
            new[] { new Panel(), new Panel() },
            new[] { new Control() },
            new[] { new Control(), new Panel() },
            new[] { new Panel(), new Control() }
        };
        // When test index 2 to 4 used, keyboard input does not reach form level
        Controls.AddRange(tests[0]);            
        // When uncommented, ensures all keyboard input reaches form level
        /*this.KeyPreview = true;              
        // Additional plumbing required along with KeyPreview to allow arrow and other reserved keys
        foreach (Control control in this.Controls)
        {
            control.PreviewKeyDown += control_PreviewKeyDown;
        }*/
    }
    void control_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
        e.IsInputKey = true;
    }
    private void Game_KeyDown(object sender, KeyEventArgs e)
    {
        // breakpoint here
        Debug.WriteLine(e.KeyCode);
    }
}

try moving the handler setup to the Form_Load event rather than the constructor. Should there not be a call to Initialize() in the constructor? I wouldn't particularly recommend removing it

If ControllableBlock inherits from Panel, it will have more event hookups and better UI interaction setup than a base Control object.

You need to make your control selectable before it can receive the focus.

Try adding the following to your constructor:

this.SetStyle(ControlStyles.Selectable, true);

And ensure that you give your form focus after it has been displayed. Or, override OnMouseDown() and call this.Focus() in it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM