I'm building a small Forms application, i've just started it.
But i have this problem:
if i put a Control to the form, the KeyDown event is not firing. I'm aware of the KeyPreview
property, and set it to true. But that didn't helped... :( I also tried to set the
focus to the main form, no success either.
Any thoughts?
Edit:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
KeyDown += new KeyEventHandler(Form1_KeyDown);
this.KeyPreview = true;
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Left: MessageBox.Show("Left");
break;
case Keys.Right: MessageBox.Show("Right");
break;
}
}
}
I already commented my solution, but I also post it as an answer, so it can be easily found.
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Left:
// left arrow key pressed
return true;
case Keys.Right:
// right arrow key pressed
return true;
case Keys.Up:
// up arrow key pressed
return true;
case Keys.Down:
// down arrow key pressed
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
If you were on WPF, you could easily catch the required events, because WPF uses routed event system to dispatch events. In winforms, I recomment one of these two ways:
1. Using Application.AddMessageFilter Method:
Define a Message Filter class:
public class KeyMessageFilter : IMessageFilter
{
private enum KeyMessages
{
WM_KEYFIRST = 0x100,
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101,
WM_CHAR = 0x102,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105,
WM_SYSCHAR = 0x0106,
}
[DllImport("user32.dll")]
private static extern IntPtr GetParent(IntPtr hwnd);
// We check the events agains this control to only handle
// key event that happend inside this control.
Control _control;
public KeyMessageFilter()
{ }
public KeyMessageFilter(Control c)
{
_control = c;
}
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == (int)KeyMessages.WM_KEYDOWN)
{
if (_control != null)
{
IntPtr hwnd = m.HWnd;
IntPtr handle = _control.Handle;
while (hwnd != IntPtr.Zero && handle != hwnd)
{
hwnd = GetParent(hwnd);
}
if (hwnd == IntPtr.Zero) // Didn't found the window. We are not interested in the event.
return false;
}
Keys key = (Keys)m.WParam;
switch (key)
{
case Keys.Left:
MessageBox.Show("Left");
return true;
case Keys.Right:
MessageBox.Show("Right");
return true;
}
}
return false;
}
}
So you have a class that every message in Windows Forms passes through it. You can do whatever you want with the event. If PreFilterMessage method returns true, it means that the event should not be dispatched to it's respcetive control.
(Note that the values in the Keys enumeration is almost idential to virtual key codes)
Before this works, you have to add it to the application's message filters:
public partial class Form1 : Form
{
// We need an instance of the filter class
KeyMessageFilter filter;
public Form1()
{
InitializeComponent();
filter = new KeyMessageFilter(panel1);
// add the filter
Application.AddMessageFilter(filter);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
// remove the filter
Application.RemoveMessageFilter(filter);
}
}
The filter is only active in the lifetime of the Form1.
Notice: This will catch events in any form! If you want it to work for only one form, pass the form to the filter class, and compare its Handle property with m.HWnd in PreFilterMessage
2. Using Windows Hooks:
This is a more advanced and complicated (and low level) approach. And it requires more code. I've wrote a HookManager class that makes the process very simple. I'm gonna publish the class to github and write an article about it.
The reason of the behaviour you observe is that special keys like TAB, UP/DOWN/LEFT/RIGHT ARROW, PAGE UP/DOWN, HOME, END, etc. are often considered "Input Keys" by common controls.
For example, ARROW keys are considered "Input Keys" by the TabControl as these keys allows you to change the selected TabPage. A similar behaviour is present with a multiline TextBox where the ARROWS keys allows you to move the text cursor.
I assume that the Rumba Mainframe control you have does the same thing for the same reasons. You can try overriding it and changing the implementation of the IsInputKey method or handling the PreviewKeyDown event and setting the IsInputKey property to true.
Please see the documentation of the Control.IsInputKey Method and Control.PreviewKeyDown Event for further details
The arrow keys are one kind of special key that are automatically handled by Controls.
So if you want to make them raise the KeyDown event you could:
1) Override the isInputKey method in every control of your form
OR
2) Handle the PreviewKeyDown event and set the IsInputKey property to true
More info can be found here.
I know WonderCsabo solved his problem already but someone else put a bounty on it because is having the same problem and no answer was selected.
WonderCsabo please post your solution as answer as well.
Related
I'm looking for a best way to implement common Windows keyboard shortcuts (for example Ctrl+F, Ctrl+N) in my Windows Forms application in C#.
The application has a main form which hosts many child forms (one at a time). When a user hits Ctrl+F, I'd like to show a custom search form. The search form would depend on the current open child form in the application.
I was thinking of using something like this in the ChildForm_KeyDown event:
if (e.KeyCode == Keys.F && Control.ModifierKeys == Keys.Control)
// Show search form
But this doesn't work. The event doesn't even fire when you press a key. What is the solution?
You probably forgot to set the form's KeyPreview property to True. Overriding the ProcessCmdKey() method is the generic solution:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == (Keys.Control | Keys.F)) {
MessageBox.Show("What the Ctrl+F?");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
On your Main form
Set KeyPreview to True
Add KeyDown event handler with the following code
private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.N)
{
SearchForm searchForm = new SearchForm();
searchForm.Show();
}
}
The best way is to use menu mnemonics, i.e. to have menu entries in your main form that get assigned the keyboard shortcut you want. Then everything else is handled internally and all you have to do is to implement the appropriate action that gets executed in the Click event handler of that menu entry.
You can even try this example:
public class MDIParent : System.Windows.Forms.Form
{
public bool NextTab()
{
// some code
}
public bool PreviousTab()
{
// some code
}
protected override bool ProcessCmdKey(ref Message message, Keys keys)
{
switch (keys)
{
case Keys.Control | Keys.Tab:
{
NextTab();
return true;
}
case Keys.Control | Keys.Shift | Keys.Tab:
{
PreviousTab();
return true;
}
}
return base.ProcessCmdKey(ref message, keys);
}
}
public class mySecondForm : System.Windows.Forms.Form
{
// some code...
}
If you have a menu then changing ShortcutKeys property of the ToolStripMenuItem should do the trick.
If not, you could create one and set its visible property to false.
From the main Form, you have to:
Be sure you set KeyPreview to true( TRUE by default)
Add MainForm_KeyDown(..) - by which you can set here any shortcuts you want.
Additionally,I have found this on google and I wanted to share this to those who are still searching for answers. (for global)
I think you have to be using user32.dll
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x0312)
{
/* Note that the three lines below are not needed if you only want to register one hotkey.
* The below lines are useful in case you want to register multiple keys, which you can use a switch with the id as argument, or if you want to know which key/modifier was pressed for some particular reason. */
Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF); // The key of the hotkey that was pressed.
KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF); // The modifier of the hotkey that was pressed.
int id = m.WParam.ToInt32(); // The id of the hotkey that was pressed.
MessageBox.Show("Hotkey has been pressed!");
// do something
}
}
Further read this http://www.fluxbytes.com/csharp/how-to-register-a-global-hotkey-for-your-application-in-c/
Hans's answer could be made a little easier for someone new to this, so here is my version.
You do not need to fool with KeyPreview, leave it set to false. To use the code below, just paste it below your form1_load and run with F5 to see it work:
protected override void OnKeyPress(KeyPressEventArgs ex)
{
string xo = ex.KeyChar.ToString();
if (xo == "q") //You pressed "q" key on the keyboard
{
Form2 f2 = new Form2();
f2.Show();
}
}
In WinForm, we can always get the Control Key status by:
bool IsCtrlPressed = (Control.ModifierKeys & Keys.Control) != 0;
The VB.NET version of Hans' answer.
(There's a ProcessCmdKey function template in Visual Studio.)
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
If (keyData = (Keys.Control Or Keys.F)) Then
' call your sub here, like
SearchDialog()
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
End Class
Hey stackoverflow community. I have a question that I've been trying to figure out for several days now and I was hoping for some help and general advice.
I have been developing a small application. The main form has a few controls some of which open dialog boxes: an OpenFileDialog, SaveFileDialog, and then a custom dialog for a specific task. The problem arose in the custom dialog box. The basic functionality in a part of the custom dialog box was the ability to click a button that would allow the user to press a key, that key would then be "read" and input into a variable for later use.
My initial thought was to override WndProc and have a check for a bool in it, if the bool was true, the data from a WM_KEYDOWN message would be stored in a variable and the bool would be set to false. I quickly figured out that doesn't work since Dialog Boxes don't get messages like a normal form does.
My question is what is the best/recommended way to achieve this functionality? I've tried some ad hoc workarounds like just using a regular form and disabling the main form while the second form is active, but that didn't work either and I figured it would be better to ask for advice before I continued.
Any help is greatly appreciated.
Edit
Here is some code to demonstrate my current issue
This has quite a few lines removed since I don't think it would be helpful to post hundreds of lines in here
internal class GUI
{
// This class has all the code for the main form
// this is one of the Event Handlers that calls the second dialog box
private static void addItemAbove_Clicked(object sender, EventArgs e)
{
ActionItemDialog aid = new ActionItemDialog();
if(aid.ShowDialog() == DialogResult.OK)
{
ActionList.Items.Insert(list.SelectedIndex, aid.ActionItem);
}
}
}
internal class ActionItemDialog : Form
{
// This class is a custom dialog box for the user to input some data
private bool keyCaptureOn = false;
// This event handler is attached to a button on the dialog box
private void getKey_Clicked(object sender, EventArgs e)
{
getKey.Text = "Press Any Key";
keyCaptureOn = true;
}
// This is the way I was originally trying to get the keypress
protected override void WndProc(ref Message m)
{
if(m.Msg == 0x100)
{
if(keyCaptureOn)
{
// have never been able to make it in this.
}
}
base.WndProc(ref m);
}
}
Also it is worthy to note, I have already tried what all the other questions about this have recommended. I've tried setting KeyPreview to true. I've also made a regular form and used Form.Show() and had it emulate a Modal Dialog but I was still never able to get into that second if statement.
In your ActionItemDialog form, you can add a filter via IMessageFilter to trap keypresses.
When we want to trap a key, we call filter.trapKey = true;, which will fire off a custom Key() event when the user presses a key.
When the form is dismissed, we remove the filter and event subscription.
Here's an example:
public partial class ActionItemDialog : Form
{
private MyFilter filter;
public ActionItemDialog()
{
InitializeComponent();
this.FormClosing += ActionItemDialog_FormClosing;
filter = new MyFilter();
filter.Key += Filter_Key;
Application.AddMessageFilter(filter);
}
private class MyFilter : IMessageFilter
{
public bool trapKey = false;
public delegate void dlgKey(int key);
public event dlgKey Key;
public bool PreFilterMessage(ref Message m)
{
if (trapKey && m.Msg == 0x100)
{
trapKey = false;
Key?.Invoke((int)m.WParam);
return true;
}
return false;
}
}
private void getKey_Click(object sender, EventArgs e)
{
getKey.Text = "Press Any Key";
filter.trapKey = true;
}
private void Filter_Key(int key)
{
getKey.Text = key.ToString();
}
private void ActionItemDialog_FormClosing(object sender, FormClosingEventArgs e)
{
if (filter != null)
{
Application.RemoveMessageFilter(filter);
filter.Key -= Filter_Key;
filter = null;
}
}
}
Note that the filter will only trap key presses when YOUR app is in focus (any form of your app). So if the user clicked to trap a key press, but then switches to another window before pressing the key, then it will not be captured.
To capture a keystroke when ANY application is in focus, you'd have to wire up a low level keyboard hook via WH_KEYBOARD_LL. This would be overkill since I'd expect the key press to be captured only when that app is in focus. This could be an alternate approach, though, if you need to trap key presses globally, as registering a hotkey fails if another app already took the combo you want.
I've created custom button derived from a normal .Net button and have added the following property to add a short cut key combination:
public Keys ShortCutKey { get; set; }
I want this combination to fire the click event of the button but have no idea how to implement this when the button is placed on a form. I know the standard way of doing a button shortcut is to use the & before the short cut character but I need to use a key combination.
Any ideas?
Many Thanks
Override the form's ProcessCmdKey() method to detect shortcut keystrokes. Like this:
private bool findShortCut(Control.ControlCollection ctls, Keys keydata) {
foreach (Control ctl in ctls) {
var btn = ctl as MyButton;
if (btn != null && btn.ShortCutKey == keydata) {
btn.PerformClick();
return true;
}
if (findShortCut(ctl.Controls, keydata)) return true;
}
return false;
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (findShortCut(this.Controls, keyData)) return true;
return base.ProcessCmdKey(ref msg, keyData);
}
Where MyButton is assumed to be your custom button control class.
I'm assuming you are using WinForms, given that the ampersand character is used in WinForms control captions to denote the shortcut character. If that is the case, then you can use the Button.PerformClick() method on a WinForms Button in order to fire the Click event manually.
If this is not the case and you are, in fact, using WPF; then take a look at the link Dmitry has posted in his comment for WPF Input Bindings.
I am implementing a search function in a windows form in c#. I have set KeyPreviewto true on the form and have added an event handler for KeyDown so I can catch things like ctrl+f, esc and enter.
I am catching these keys just fine and I'm able to make my text box appear, but I am unable to type into the box. All of the keys are going to PortsTraceForm_KeyDown(...) but they never make it to the text box. According to the msdn page about KeyPreview, setting e.Handled to false should cause the event to pass to the view in focus (the text box), but this isn't happening. I have not registered a KeyDown event for the text box, so it should be using the default behavior. Have I missed something?
KeyDown event:
private void PortsTraceForm_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
e.Handled = false;
if (e.KeyData == (Keys.F | Keys.Control)) // ctrl+f
{
e.Handled = true;
ShowSearchBar();
}
else if (e.KeyCode == Keys.Escape) // esc
{
e.Handled = true;
HideSearchBar();
}
else if (e.KeyCode == Keys.Enter) // enter
{
if (searchPanel.Visible)
{
e.Handled = true;
if (searchShouldClear)
SearchStart();
else
SearchNext();
}
}
}
show search bar:
private void ShowSearchBar()
{
FindBox.Visible = true;
FindBox.Focus(); // focus on text box
}
hide search bar:
private void HideSearchBar()
{
this.Focus(); // focus on form
FindBox.Visible = false;
}
Your TextBox likely does not have focus even though you are calling Focus(). From the documentation:
Focus is a low-level method intended primarily for custom control authors. Instead, application programmers should use the Select method or the ActiveControl property for child controls, or the Activate method for forms.
You can check the return value of Focus() for success, but I have had little luck in the past using that method to set focus to an arbitrary control. Instead, try using the method that the documentation suggests, i.e., call Select().
EDIT:
Nevermind (though it's still valid advice), I think I see your problem:
e.SuppressKeyPress = true
Why are you doing this? Again, from the docs:
[SuppressKeyPress] Gets or sets a value indicating whether the key event should be passed on to the underlying control
So you are intentionally preventing the TextBox from getting key events. If you want to pass the event through you shouldn't be setting that property to false.
try this example , of overrides method.
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
// your code here
// this is message example
MessageBox.Show(keyData.ToString());
return base.ProcessCmdKey(ref msg, keyData);
}
Regards.
I am building an application where all the key input must be handled by the windows itself.
I set tabstop to false for each control witch could grab the focus except a panel (but I don't know if it has effect).
I set KeyPreview to true and I am handling the KeyDown event on this form.
My problem is that sometimes the arrow key aren't responsive anymore:
The keydown event is not fired when I pressed only an arrow key.
The keydown event is fired if I press an arrow key with the control modifier.
Have you an idea why my arrow key suddenly stop firing event?
I was having the exact same problem. I considered the answer #Snarfblam provided; however, if you read the documentation on MSDN, the ProcessCMDKey method is meant to override key events for menu items in an application.
I recently stumbled across this article from microsoft, which looks quite promising: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.previewkeydown.aspx. According to microsoft, the best thing to do is set e.IsInputKey=true; in the PreviewKeyDown event after detecting the arrow keys. Doing so will fire the KeyDown event.
This worked quite well for me and was less hack-ish than overriding the ProcessCMDKey.
protected override bool IsInputKey(Keys keyData)
{
switch (keyData)
{
case Keys.Right:
case Keys.Left:
case Keys.Up:
case Keys.Down:
return true;
case Keys.Shift | Keys.Right:
case Keys.Shift | Keys.Left:
case Keys.Shift | Keys.Up:
case Keys.Shift | Keys.Down:
return true;
}
return base.IsInputKey(keyData);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
switch (e.KeyCode)
{
case Keys.Left:
case Keys.Right:
case Keys.Up:
case Keys.Down:
if (e.Shift)
{
}
else
{
}
break;
}
}
I'm using PreviewKeyDown
private void _calendar_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e){
switch (e.KeyCode){
case Keys.Down:
case Keys.Right:
//action
break;
case Keys.Up:
case Keys.Left:
//action
break;
}
}
See Rodolfo Neuber's reply for the best answer
(My original answer):
Derive from a control class and you can override the ProcessCmdKey method. Microsoft chose to omit these keys from KeyDown events because they affect multiple controls and move the focus, but this makes it very difficult to make an app react to these keys in any other way.
Unfortunately, it is quite difficult to accomplish this with the arrow keys, due to restrictions in KeyDown events. However, there are a few ways to get around this:
As #Snarfblam stated, you can override the ProcessCmdKey method, which retains the ability to parse arrow key presses.
As the accepted answer from this question states, XNA has a built-in method called Keyboard.GetState(), which allows you to use arrow key inputs. However, WinForms doesn't have this, but it can be done through a P/Invoke, or by using a class that helps with it.
I recommend trying to use that class. It's quite simple to do so:
var left = KeyboardInfo.GetKeyState(Keys.Left);
var right = KeyboardInfo.GetKeyState(Keys.Right);
var up = KeyboardInfo.GetKeyState(Keys.Up);
var down = KeyboardInfo.GetKeyState(Keys.Down);
if (left.IsPressed)
{
//do something...
}
//etc...
If you use this in combination with the KeyDown event, I think you can reliably accomplish your goal.
I had a similar issue when calling the WPF window out of WinForms.
var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
ElementHost.EnableModelessKeyboardInterop(wpfwindow);
wpfwindow.Show();
However, showing window as a dialog, it worked
var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
ElementHost.EnableModelessKeyboardInterop(wpfwindow);
wpfwindow.ShowDialog();
Hope this helps.
In order to capture keystrokes in a Forms control, you must derive a new class that is based on the class of the control that you want, and you override the ProcessCmdKey().
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
//handle your keys here
}
Example :
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
//capture up arrow key
if (keyData == Keys.Up )
{
MessageBox.Show("You pressed Up arrow key");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
Full source...Arrow keys in C#
Vayne
The best way to do, I think, is to handle it like the MSDN said on
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.previewkeydown.aspx
But handle it, how you really need it. My way (in the example below) is to catch every KeyDown ;-)
/// <summary>
/// onPreviewKeyDown
/// </summary>
/// <param name="e"></param>
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
e.IsInputKey = true;
}
/// <summary>
/// onKeyDown
/// </summary>
/// <param name="e"></param>
protected override void OnKeyDown(KeyEventArgs e)
{
Input.SetFlag(e.KeyCode);
e.Handled = true;
}
/// <summary>
/// onKeyUp
/// </summary>
/// <param name="e"></param>
protected override void OnKeyUp(KeyEventArgs e)
{
Input.RemoveFlag(e.KeyCode);
e.Handled = true;
}
i had the same problem and was already using the code in the selected answer. this link was the answer for me; maybe for others also.
How to disable navigation on WinForm with arrows in C#?
protected override bool IsInputKey(Keys keyData)
{
if (((keyData & Keys.Up) == Keys.Up)
|| ((keyData & Keys.Down) == Keys.Down)
|| ((keyData & Keys.Left) == Keys.Left)
|| ((keyData & Keys.Right) == Keys.Right))
return true;
else
return base.IsInputKey(keyData);
}