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
Related
I have an mdiParent form in which i have a save method. Now I want that whenever i press Ctrl+S in my activeMdiChild form the parents Save method should get called. Any hint or suggestion is welcome.
Thanx in advance.
If you have a MenuStrip in the MDI Parent Form, then you don't need to do anything else than adding a menu with the Ctrl+S shortcut. Then if the user press those combination even in child form, the code for that menu of parent will execute.
But if you don't want to add a menu, then you can override ProcessCmdKey in the "MDI Parent Form" and check if the Ctrl+S combination was pressed, then perform the desired action:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.S))
{
MessageBox.Show("Handled in main form.");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
The
ProcessCmdKey
method overrides the base ContainerControl.ProcessCmdKey
implementation to provide additional handling of main menu command
keys and MDI accelerators.
Handle the KeyDown event in activeMdiChild .
private void activeMdiChild _KeyDown(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.S && e.Modifiers == Keys.Control)
{
dynamic parent = this.Parent;
parent.Save();
}
}
EDIT: as mentioned in comments, mdiParent.Save must be public
One of possible solutions:
1. Create an interface
ISave
{
void Save();
}
2. Let your parent form inherit interface
3. In your child form check for ctrl + s if so do
ISave saver = this.Parent as ISave;
if(saver != null)
saver.Save();
That's all
Is there anyway to check if a menu item event is coming from a click in the menu or from a short cut key being pressed?
I've tried adding event handlers to the key press and key down events, however these events aren't being "fired" when it's a shortcut key that's pressed (They do work as expected when it's not a shortcut key). I couldn't find anything in the sender object that was different between the menu click or shortcut click.
You can catch all key combinations by overriding ProcessCmdKey:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.F))
{
Console.WriteLine("My code ran from shortcut");
myFunction();
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void ToolStripMenuItem_click(object sender ...)
{
Console.WriteLine("My code ran from menu item");
myFunction();
}
void myFunction()
{
//your functionality here
}
Well to get help, you should post what you have tried. (Your source)
You can use a enum for this:
enum Sender
{
Shortcut,
Menu
}
void MenuEvent(Sender sender)
{
if (sender == Sender.Shortcut)
{
}
else
{
}
}
//if you click from the menu
void btnMenuClick()
{
MenuEvent(Sender.Menu);
}
//if you use shortcut
void OnShortcutEvent()
{
MenuEvent(Sender.Shortcut);
}
Edit: I guess my answer was to vague so I edited the code. I hope its more clear now, but I must say the OP should give more details as well, such as posting some code.
I see a single solution to this problem - override the ToolStripMenuItem's ProcessCmdKey method which is raised when a shortcut is processed. In this case, you can determine when a click was caused by a shortcut. Obviously, you need to use your own class of the ToolstripMenuItem instead of the standard one.
Handle the MouseDown event to process your mouse-click.
menuItem.MouseDown += new MouseEventHandler(Process_Mouse_Click_Handler);
Handle the Click event to process your shortcut.
menuItem.Click+= new EventHandler(Process_Shortcut_Handler);
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.
Is it somehow possible to prevent end users from copying text from RichTextBox in a C# application? Maybe something like adding a transparent panel above the RichTextBox?
http://www.daniweb.com/software-development/csharp/threads/345506
Re: Disable copy and paste control for Richtextbox control Two steps
to be followed to disable the Copy-Paste feature in a textbox,
1) To stop right click copy/paste, disable the default menu and
associate the textbox with an empty context menu that has no menu
items.
2) To stop the shortcut keys you'll need to override the ProcessCmdKey
method:
C# Syntax (Toggle Plain Text)
private const Keys CopyKey = Keys.Control | Keys.C;
private const Keys PasteKey = Keys.Control | Keys.V;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if((keyData == CopyKey) || (keyData == PasteKey)){
return true;
} else {
return base.ProcessCmdKey(ref msg, keyData);
}
}
You need to subclass the rich edit box control, override the WM_COPY message, and do nothing when you receive it. (Do not delegate to DefWindowProc.)
You will also have to do the same for WM_CUT.
And still anyone will be able to get the text using some utility like Spy++.
One way to doing it is make access to clipbord object and than you can manitpulate content of clipbord.
method for clearing clipbord is : Clipboard.Clear Method
You could do this (I think):
//Stick this in the KeyDown event of your RichtextBox
if (e.Control && e.KeyCode == Keys.C) {
e.SuppressKeyPress = true;
}
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.