I'm coding a text editor for part of my winforms application. It has the usual Bold, Underline, Strikeout, Italic toolbar buttons. For reasons of accessibility and workflow efficiency I want to add shortcuts as well, however.
Here's the relevant code:
private void TextInput_KeyDown(object sender, KeyEventArgs e)
{
if(e.Control)
{
switch(e.KeyCode)
{
case Keys.B: ChangeFontStyle('B');
break;
case Keys.I: e.SuppressKeypress = true;
ChangeFontStyle('I');
break;
// ... the others (U and S) look like the one for B, with the matching letters...
}
}
}
}
private voide ChangeFontStyle(char targetStyle)
{
switch(targetStyle)
{
case 'B':
if(TextInput.Selectionfont.Bold)
{
TextInput.SelectionFont = new Font(TextInput.Selectionfont, TextInput.Selectionfont.Style ^ FontStyle.Bold);
}
else
{
TextInput.SelectionFont = new Font(TextInput.Selectionfont, TextInput.SelectionFont.Style | FontStyle.Bold);
}
}
}
The others look like this, too, just with italic, underline and strikeout respectively. For three of them itorks (though if I don't " e.SuppressKeyPress on ctrl-I, an indent gets set on top of font turning italic). Just strikeout doesn't work with ctrl-S. With ctrl-shift-S it works, and the case 'S' block never executes, so ctrl-S must somehow get caught somewhere and used for something else. But I definitely don't use it elsewhere. Suggestions?
When you have a MenuStrip on the form including a menu item with Ctrl + S as ShortcutKey, then Ctrl + S will be consumed by menu item and your control will not receive the key combination.
KeyDown event of RichTextBox is too late for handling shortcut keys and MenuStrip or parent controls may consume the key combination in their ProcessCmdKey.
To handle shortcut keys for RichTextBox, use one of the following options:
Have a MenuStrip including a ToolStripMenuItem assigned with the shortcut key to its ShortcutKeys propert, then handling Click event of the menu item:
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("Handled by Save Menu");
}
Override ProcessCmdKey method of the Form:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.S))
{
MessageBox.Show("Handled by Form");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
The last option is using PreviewKeyDown event of the RichTextBox:
private void richTextBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyData == (Keys.Control | Keys.S))
{
e.IsInputKey = true;
BeginInvoke(new Action(() => {
MessageBox.Show("Handled by RichTextBox");
}));
}
}
Related
How do I set the keyboard shortcut character for a Windows Forms button with no text?
I have a Windows Forms app with some buttons with images but no text. Normally for a button, the shortcut is specified with the ampersand character (&). But what does one do when there's no text?
I know there are tricks, such as leading spaces to push the text off to the right or down. Or play with the font, forecolor, etc. But I don't like such tricks because they sometimes backfire.
Should I consider handling the shortcuts for all controls through the Key events of the form? How would this sort out which control is selected? Are form Key events filtered through the form before they're sent on to the Key events of the controls? I'm not sure what to do if I went this way.
Should I consider handling the shortcuts for all controls through the Key events of the Form?
You sure can, overriding ProcessCmdKey() in your Form(s). As you can read in the docs, it's kind of made for this (to provide additional handling of main menu command keys and MDI accelerators)
To associate a Button to a bitwise combination of Keys (the Windows Forms Keys enumerator is decorated with the [Flags] attribute), you can use a map, often a Dictionary that associates a Key to something else.
In this case, it could be a Button Control or an Action. For example:
private Dictionary<Keys, Button> accelerators = null;
protected override void OnLoad(EventArgs e) {
accelerators = new Dictionary<Keys, Button>() {
[Keys.Alt | Keys.A] = someButton,
[Keys.Alt | Keys.Control | Keys.B] = otherButton
};
base.OnLoad(e);
}
In the ProcessCmdKey override, test whether the map (Dictionary) contains the combination of keys pressed and raise the Click event of the associated Button if it's a match:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (accelerators.ContainsKey(keyData)) {
accelerators[keyData].PerformClick();
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
VB.Net version (since it's tagged):
Private accelerators As Dictionary(Of Keys, Button) = Nothing
Protected Overrides Sub OnLoad(e As EventArgs)
accelerators = New Dictionary(Of Keys, Button)() From {
{Keys.Alt Or Keys.A, someButton},
{Keys.Alt Or Keys.Control Or Keys.B, otherButton}
}
MyBase.OnLoad(e)
End Sub
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
If accelerators.ContainsKey(keyData) Then
accelerators(keyData).PerformClick()
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
This is how I do it.
set the KeyPreview to true for the form to capture keys.
then check the keys on KeyDown event on the form and trigger button1.PerformClick();
public Form1()
{
InitializeComponent();
KeyPreview = true;
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("you pressed Ctrl + S");
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
button1.PerformClick();
}
}
Hope this can help you.
Set your Form KeyPreview Property to true.
Subscribe to the KeyDown Event of the Form as below:
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
Handle the KeyDown event of Form as below:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
MessageBox.Show("Ctrl + S pressed!");
}
Complete Code:
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
MessageBox.Show("Ctrl + S pressed!");
}
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
I have a basic WinForms project where the user clicks a button. This opens another form as follows:
form2 myForm2 = new form2();
myForm2.ShowDialog();
Inside this new form, there are four buttons which represent values. The user presses the SPACE key to jump between buttons and the ENTER key to select one. When the user presses ENTER on a button I want the form to close. For this is I use 'this.Close()'. This works for absolutely fine with every key other than ENTER. I am using visual studio so I have inserted a break point and stepped over the code. The ENTER key is detected successully and I can step over the code 'this.Close()' but the window never closes. My code is a followed:
private void button1_KeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
// Change colour of the button you are on. This works fine.
}
else if (e.KeyCode == Keys.Enter)
{
this.Close();
// This will close the form with all keys other than the Enter key. Yet the enter key is
// successfully detected and the program enters this else if statement.
}
}
Any help is much appreciated, Thanks.
I'd override ProcessCmdKey() and make the space key act like the tab key to select the next control. Then just handle each the click event of each button like normal. The click handler for the buttons will fire when the user presses enter:
public partial class form2 : Form
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
Button btn = this.ActiveControl as Button;
if (btn != null)
{
if (keyData == Keys.Space)
{
// possibly do something else with "btn"?...
this.SelectNextControl(btn, true, true, true, true);
return true; // suppress default handling of space
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void button1_Click(object sender, EventArgs e)
{
// possibly set some value?
Console.WriteLine("button1");
this.DialogResult = DialogResult.OK;
}
private void button2_Click(object sender, EventArgs e)
{
// possibly set some value?
Console.WriteLine("button2");
this.DialogResult = DialogResult.OK;
}
private void button3_Click(object sender, EventArgs e)
{
// possibly set some value?
Console.WriteLine("button3");
this.DialogResult = DialogResult.OK;
}
private void button4_Click(object sender, EventArgs e)
{
// possibly set some value?
Console.WriteLine("button4");
this.DialogResult = DialogResult.OK;
}
}
An alternative approach would be to also trap the enter key in ProcessCmdKey() like this:
public partial class Form2 : Form
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
Button btn = this.ActiveControl as Button;
if (btn != null)
{
if (keyData == Keys.Space)
{
Console.WriteLine("Space -> Tab");
// possibly do something else with "btn"?...
this.SelectNextControl(btn, true, true, true, true);
return true; // suppress default handling of space
}
else if (keyData == Keys.Enter)
{
Console.WriteLine("Enter in ProcessCmdKey() for " + btn.Name);
// possibly do something else with "btn"?...
this.Close();
// < or >
this.DialogResult = DialogResult.OK;
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
Looking into signature of the handler (object sender, PreviewKeyDownEventArgs e) apparently OP is already handling PreviewKeyDown event.
I reproduced and solved the problem.
The behavior is different when you open a Form using Show or ShowDialog. Everything works as expected with Show, but when using ShowDialog, to close the form in PreviewKeyDown, you need to use one of the following options:
private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
e.IsInputKey = true;
this.Close();
// OR
// if (e.KeyData == Keys.Enter)
// this.BeginInvoke(new Action(() => this.Close()));
// OR first hide, then close, without calling BeginInvoke
// this.Hide();
// this.Close();
}
There should be something about model message loop. I didn't traced in details, but you may find the following links useful:
LocalModalMessageLoop in Application
CheckCloseDialog in Form
WMClose in Form
Just as a side note: If the Enter should be handled at form level, then setting AcceptButton of the form or overriding ProcessKeyDown or ProcessDialogKey of the form is the way to go.
I have a MaskedTextBox that formats text to look like (###) ###-####
After entering the first 3 digits, they like to press "TAB" to the next set. Unfortunately by pressing TAB, they are in the next field.
So my boss asked me to modify the application so that the users remain in the same field but the cursor is in the next group.
private void maskedTextBoxHomePhone_KeyPress(object sender, KeyPressEventArgs e)
{
MaskedTextBox mtb = (MaskedTextBox)sender;
if (e.KeyChar == (char)Keys.Tab)
{
if (mtb.TextLength == 3)
{
mtb.SelectionStart = 4;
}
}
}
I've also tried
private void maskedTextBoxHomePhone_KeyDown(object sender, KeyEventArgs e)
{
MaskedTextBox mtb = (MaskedTextBox)sender;
if (e.KeyCode == Keys.Tab)
{
if (mtb.TextLength == 3)
{
mtb.SelectionStart = 4;
}
}
}
Tabs have a special meaning, that will cause the focus to change, so the event handler won't get called.
You could work around this by using the Leave event of a text box and counting the textlength that you have stored in some local variable:
private void maskedTextBoxHomePhone_Leave(object sender, EventArgs e)
{
if (_mtbTextLength == 3) { //change selection start and goes back to masked text box }
}
Anyway, actually I would try to convince my boss otherwise. Do you
really need this? Tabs are always used to change fields, you can get
your users confused.
Another option would be to change the Tab behaviour by overriding the ProcessCmdKey:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Tab)
{
//Do something
}
}
I am working in a webform and have a form with a tab bar on it. Each tab has multiple text boxes in it. I have the tab indexes incremented, starting with 1 for each tab. I want to tab from tab to tab if the user hits the end of the form and hits tab.
I used the leave method and changed the tabs for my tab control the only problem is if I didn't hit tab and say I click to another control on that tab it will still shoot over to the new tab.
I figure a way to solve this would be to listen for the tab key press and if the key press is tab on leave then change the form to the other tab, I just can't seem to get it to work though. I have tried with keypress and keydown but neither will pick up that tab as a key. If I was to say click or hit start typing it will trigger the events but tab will not.
Any suggestions?
I have tried these and none of these event would even trigger.
private void afsiTxtDaysForTempOEpriceOverrides_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 11)
{
afsiTxtDaysForTempOEpriceOverrides_Leave(sender, e);
}
}
private void afsiTxtDaysForTempOEpriceOverrides_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == "11")
{
afsiTxtDaysForTempOEpriceOverrides_Leave(sender, e);
}
}
private void afsiChkSalesBaseCostUpdate_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 11)
{
afsiChkSalesBaseCostUpdate_Leave(sender, e);
}
}
private void afsiChkSalesBaseCostUpdate_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == 11)
{
afsiChkSalesBaseCostUpdate_Leave(sender, e);
}
}
EDIT: Found out that the page is using UltraWinTabControl from Infragistics so maybe this is causing some issues with the tabbing.
I ended up needing to override ProcessCmdKey now I face a new problem that is kind of related but not particular to this so I will add it as a comment if I get my answer.
private bool isTab = false;
private bool isShiftTab = false;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Tab)
{
isTab = true;
ShiftTab.Append("Tab");
}
else
{
isTab = false;
}
return base.ProcessCmdKey(ref msg, keyData);
}
You will need to have the MultiLine property set to true, and AcceptsTab also set to true.
Or use e.KeyCode instead or e.KeyData, it worked for me
if (e.KeyCode == Keys.Tab | e.KeyData == Keys.Enter)