Reputation:
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
Reputation: 12330
I was able to reproduce a similar issue (which is hopefully related..)
Explanation:
CanSelect==true
are selectable for keyboard inputControl()
is selectable, one of Panel()
is notCheck 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
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
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
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