Reputation: 67
I need to perform a certain method as long as an arrow key is being held down. Now, for regular arrow key presses in Visual studio 2012, using OnKeyDown
did not work, so I needed to use ProcessCmdKey
, which works like a charm. Is there a way to use ProcessCmdKey
to detect when an arrow has been released / being held down?
I've taken both Markus' and Hans' advice, and combined the two ideas. I used the ProcessKeyPreview
method, but I am still having some issues. When I hold down any arrow key, this method will not detect that WM_KEYDOWN
has happened... but as soon as I release my finger from the key, it actually DOES notice that WM_KEYUP
has happened.
The interesting this is that when I hold down any other key (i.e. letter 'S'), it properly recognizes when it has been pressed and released. I've posted a fragment of my code below:
const int WM_KEYUP = 0x0101;
const int WM_KEYDOWN = 0x0100;
protected override bool ProcessKeyPreview(ref Message m)
{
int msgVal = m.WParam.ToInt32();
if (m.Msg == WM_KEYDOWN)
{
switch ((Keys)msgVal) {
case Keys.Down:
Console.WriteLine("down pressed"); //not detected
break;
case Keys.S:
Console.WriteLine("S pressed!"); //detected
break;
}
}
if (m.Msg == WM_KEYUP)
{
switch ((Keys)msgVal)
{
case Keys.Down:
Console.WriteLine("down released"); //detected
break;
case Keys.S:
Console.WriteLine("s released!"); //detected
break;
}
}
return base.ProcessKeyPreview(ref m);
}
Upvotes: 3
Views: 1777
Reputation: 761
You can overload ProcessKeyPreview instead, which will let you see the WM_KEYDOWN message as well as the WM_KEYUP message. Be sure to turn on KeyPreview for the form.
WM_KEYDOWN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646280(v=vs.85).aspx
WM_KEYUP:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646281(v=vs.85).aspx
Example:
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
this.KeyPreview = true;
}
const int WM_KEYUP = 0x0101;
const int WM_KEYDOWN = 0x0100;
protected override bool ProcessKeyPreview(ref Message m)
{
switch (m.Msg)
{
case WM_KEYDOWN:
Console.WriteLine(m.LParam);
break;
case WM_KEYUP:
Console.WriteLine(m.LParam);
break;
}
return base.ProcessKeyPreview(ref m);
}
}
Upvotes: 2
Reputation: 941685
You cannot see the KeyUp event with ProcessCmdKey(), it was made to only handle KeyDown events. You'll need to tackle this at a much lower level if the form contains controls. The trick is to intercept the message before Winforms sends it through the normal key-handling and WndProc chain. That requires implementing the IMessageFilter interface. Like this:
public partial class Form1 : Form, IMessageFilter { // NOTE: added IMessageFilter
public Form1() {
InitializeComponent();
Application.AddMessageFilter(this);
}
protected override void OnFormClosed(FormClosedEventArgs e) {
Application.RemoveMessageFilter(this);
base.OnFormClosed(e);
}
bool IMessageFilter.PreFilterMessage(ref Message m) {
// Trap WM_KEYUP/DOWN for Keys.Down key
if ((m.Msg == 0x100 || m.Msg == 0x101) && (Keys)m.WParam.ToInt32() == Keys.Down) {
bool repeat = (m.LParam.ToInt32() & (1 << 30)) != 0;
bool down = m.Msg == 0x100;
// But only for this form
Form form = null;
var ctl = Control.FromHandle(m.HWnd);
if (ctl != null) form = ctl.FindForm();
if (form == this) {
OnCursorDown(down, repeat & down);
return true;
}
}
return false;
}
private void OnCursorDown(bool pressed, bool repeat) {
// etc..
}
}
Upvotes: 6