Key Events: Processcmdkey

Key Events: ProcessCmdKey

The Message structure passed to ProcessCmdKey() contains the WINAPI message number in its Msg property:

  • WM_KEYDOWN is 0x100 (256),
  • WM_KEYUP is 0x101 (257),
  • WM_CHAR (roughly equivalent to KeyPress) is 0x102 (258),
  • WM_SYSKEYDOWN is 0x104 (260),
  • WM_SYSKEYUP is 0x105 (261).

Concerning your question about KeyPress, it is true that non-character keys such as the arrow keys do not generate WM_CHAR messages internally, but they do generate WM_KEYDOWN, and that message is also sent multiple times for repeated input.

Also note that I'm not sure ProcessCmdKey() is the right method to achieve what you want. The documentation describes it as only handling main menu command keys and MDI accelerators, which may only be a subset of the keys you want to catch. You might want to override ProcessKeyPreview() instead, which handles all the keyboard messages received by child controls.

Hold Arrow Key Down Event with ProcessCmdKey

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..
}
}

ProcessCmdKey firing twice, WinForms

I believe the reason your ProcessCmdKey is firing multiple times is this:

The ProcessCmdKey method first determines whether the control has a ContextMenu, and if so, enables the ContextMenu to process the command key. If the command key is not a menu shortcut and the control has a parent, the key is passed to the parent's ProcessCmdKey method. The net effect is that command keys are "bubbled" up the control hierarchy. In addition to the key the user pressed, the key data also indicates which, if any, modifier keys were pressed at the same time as the key. Modifier keys include the SHIFT, CTRL, and ALT keys.

per MSDN: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.processcmdkey(v=vs.110).aspx

In your method there, I see you're checking which control is focused. So this method is firing for that control, then for the parent control (which I assume is the form) because it's unlikely that focused control has has a shortcut key or whatever.

Stop a key from firing an event in C# using ProcessCmdKey?

You need to return true when you don't want the default action to be carried out.

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Right)
{

numericUpDown1.Value = Convert.ToDecimal(numericUpDown1.Value + 1);
return true;
}
if (keyData == Keys.Left)
{
try
{
numericUpDown1.Value = Convert.ToDecimal(numericUpDown1.Value - 1);
return true;
}
catch { }
}
}

WinForms - Capturing the combination of key presses using ProcessCmdKey VS KeyDown

Set the KeyPreview property of our form to true. The summary of this property says: "Gets or sets a value indicating whether the form will receive key events before the event is passed to the control that has focus.". Then use the KeyUp event. Unless KeyPressed it gives also information on special keys like control keys.

Capture Key Sequence via ProcessCmdKey

In order to achieve the functionality you want you simply need to keep track of the sequence of KeyPress events.

You can create a class to keep track of the last key combination that was pressed in ProcessCmdKey. If that particular combination does not match a mapped command but it is the first element of a sequence you can store it in your class. Then the next time ProcessCmdKey is activated check your new KeyPressTracker class to determine if a sequence has been started. If it has then check if the newly pressed key combination is the second element of one you specify. Please see the pseudocode example below:

Step 1: ProcessCmdKey is activated. The key combination is Ctrl+R, this does not match a command that you want to process but it is the first element of a sequence that you want to use (Ctrl+R+M).

Step 2: Store this key-press in a new class you created to keep track of the last key-press.

KeyPressTracker.Store(KeyCode, Modifiers);

Step 3: ProcessCmdKey is activated a second time. This time, the key combination is Ctrl+M which is not a key-press we're looking for but is the second element of a sequence. We check the last stored keypress using the new KeyPressTracker class. This will allow you to match a "sequence" such as Ctrl+R and Ctrl+M.

var lastKeyPress = KeyPressTracker.GetLastKeyPress();

if (lastKeyPress == "Ctrl+R" && currentKeyPress == "Ctrl+M")
{
// Show Refactor dialog
}

ProcessCmdKey - wait for KeyUp?

I thought it'd be easy, just look at the key's repeat count. Didn't work though if modifiers are used. You'll need to see the key go up as well, that requires implementing IMessageFilter. This worked:

public partial class Form1 : Form, IMessageFilter {
public Form1() {
InitializeComponent();
Application.AddMessageFilter(this);
this.FormClosed += (s, e) => Application.RemoveMessageFilter(this);
}
bool mRepeating;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == (Keys.Control | Keys.F) && !mRepeating) {
mRepeating = true;
Console.WriteLine("What the Ctrl+F?");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
public bool PreFilterMessage(ref Message m) {
if (m.Msg == 0x101) mRepeating = false;
return false;
}
}

ProcessCmdKey does not pass Enter Key

I am unsure as to what you need. You have not defined what "process" the enter key means. My best guess:

1- Do you want the enter key to perform a new line in your text boxes? Change the AcceptsReturn property from False to True, and your text box should allow you to use the enter key to create a new line.

2 - Use the enter key to tab to the next control? Add a keyDown event to a text box and do something like this:

switch (e.KeyCode)
{
case Keys.Enter:
SendKeys.Send("{TAB}");
break;
}

Can't get Windows Forms Key combinations if one of the Keys is Alt, Control, or Shift

The KeyData property exposes the key that was pressed as well as the modifiers that are active. So you'd use it like this:

private void Form1_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyData == (Keys.Shift | Keys.W) || e.KeyData == (Keys.Shift | Keys.S)) {
Console.WriteLine("combination");
}
}

You can do it your way as well, but then you have to use a different property, KeyCode. It exposes only the key code without the modifier keys:

private void Form1_KeyDown(object sender, KeyEventArgs e) {
if (e.Shift) {
Console.WriteLine("shift");
if (e.KeyCode == Keys.W || e.KeyCode == Keys.S) {
Console.WriteLine("combination");
}
}
}

Which one you'd use is entirely up to you. Do however keep in mind that using the form's KeyDown event is not very correct. It also requires setting the KeyPreview property to true. That's a pretty ugly VB6 compatibility property, you cannot see every possible keystroke. The navigation keys are filtered, like it was done in VB6. The native Winforms way is to override the ProcessCmdKey() method instead. Which does require you to work with KeyData.



Related Topics



Leave a reply



Submit