user2032433
user2032433

Reputation:

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.

Upvotes: 2

Views: 18329

Answers (4)

Peter Wishart
Peter Wishart

Reputation: 12330

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);
    }
}

Upvotes: 4

Nogard
Nogard

Reputation: 1789

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);
    }

Upvotes: 4

DiskJunky
DiskJunky

Reputation: 4991

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.

Upvotes: 0

Matthew Watson
Matthew Watson

Reputation: 109842

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.

Upvotes: 0

Related Questions