I have just found out that we can't use the KeyDown event directly with a PictureBox. So I have to change my strategy.
I decided to add the Keydown event to the actual form:
private void FullColourPaletteForm_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Left:
{
MessageBox.Show("Left");
e.Handled = true;
return;
}
}
}
Doesn't get executed. I see no message box when I press the left allow. Instead (and rightly so) it just moves the cusor from control to control.
I was hoping to be able to mimic some kind of cursor support for the block of colour by intercepting the arrow keys inside the picture box.
I am not sure of the best way forward. I don't want to break the standard dialogue functionality of moving between controls, but I want to now include suipport for detectign keys so I can add my code to move my block of colour.
Can it be done? Not sure why my event is not getting triggered in the form anyway.
I saw this question. So I tried setting my form KeyPreview property. No joy. I also looked at ProcessCmdKey but it doesn't seem right for the issue in hand.
Update:
If I try to follow the idea in the comments and create a SelectablePictureBox control, it looks like this:
I have two issues. 1. I still can't seem to work out how to handle the keydown event on my pictureBox object itself. I am reluctant to manually add any handlers to the designer file incase my changes get lost.
Also, when doing general control nagivation on the form with cursor keys it does not seem to know about this control.
If you want to handle arrow keys at form level, you can override the form's ProcessCmdKey function this way:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Left)
{
MessageBox.Show("Left");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
But in general it's better to create a custom paint selectable control like this rather than putting such logic at form level. Your control should contain such logic.
Note
OP: I have just found out that we can't use the KeyDown event directly
with a PictureBox
As mentioned by Hans in comments, the PictureBox control is not selectable and can not be focused by default and you can not handle keyboard events for the control.
But you can force it to be selectable and support keyboard events this way:
using System;
using System.Reflection;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.pictureBox1.SetStyle(ControlStyles.Selectable, true);
this.pictureBox1.SetStyle(ControlStyles.UserMouse, true);
this.pictureBox1.PreviewKeyDown +=
new PreviewKeyDownEventHandler(pictureBox1_PreviewKeyDown);
}
void pictureBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyData == Keys.Left)
MessageBox.Show("Left");
}
}
public static class Extensions
{
public static void SetStyle(this Control control, ControlStyles flags, bool value)
{
Type type = control.GetType();
BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
MethodInfo method = type.GetMethod("SetStyle", bindingFlags);
if (method != null)
{
object[] param = { flags, value };
method.Invoke(control, param);
}
}
}
At least knowing this approach as a hack you can reuse the extension method to enable or disable some styles on controls in future.
Related
I need to catch the F1 KeyEvent when it fires regardless of what has current focus. Currently I have a base class that defines the method that is intended to fire upon F1 keyup. In the child class I am listening for the .KeyUp event and will invoke the parent.
//Base class:
protected void GetHelp(String helpIndexParam)
{
// Logic
}
//Child Class:
//Declared in constructor of child class
KeyPreview = true;
this.KeyUp += new System.Windows.Forms.KeyEventHandler(KeyEvent);
private void KeyEvent(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
base.GetHelp("20");
}
}
Currently, a child form opens and I press F1, nothing happens. Only when I click on a control on the form and then press F1 does the GetHelp("") function execute.
Any suggestions appreciated. Cheers.
In this scenario, you may use ProcessCmdKey method in the parent form or each child form:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == Keys.F1) {
// Show help;
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
Or since you are looking for F1 key, you can use the HelpRequest event:
public Form1() {
InitializeComponent();
this.HelpRequested += Form1_HelpRequested;
}
private void Form1_HelpRequested(object sender, HelpEventArgs hlpevent) {
// Show Help
}
I was able to get it working. I learned that because we're using an MDI, the MDI itself was apparently consuming any attempt of catching an F1 click in the child form. I basically had to set it up so that the KeyUp event was defined in the parent form as opposed to the child form, and the child form would simply populate the needed properties from the parent form. That's how I was able to pass a the necessary info to the parent. Also, I had to set focus on a control on the child form just so the whole thing could come together.
I appreciate all the help guys. You all pointed me down the right path in the long run. Cheers.
I am writing a C# WinForms application, .NET 4.0.
I have a WinForms Control on a Form. After user starts typing something using keyboard, another Control appears. That Control is some kind of text input. I'd like to send user input to that Control. Of course, after it gets focused, it receives all user keyboard input. But as user starts typing before Control appears, I have to pass first KeyDown event to that control after I call it's Show and Focus methods.
SendKeys.Send method is a way of doing something similar. But that is so complicated, and seems to be unsafe. I have just a value of Keys enumeration from KeyData property of KeyEventArgs, I'd like to use it, not transform it to some strange string.
Is there any good way to pass KeyDown event from one control to another?
You can use PreviewKeyDown on Form.
Suppose you want to send keyboard inputs to TextBox textBox1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.PreviewKeyDown+= Form1_OnPreviewKeyDown;
textBox1.Visible = false;
}
private bool _textboxEnable = false;
private void Form1_OnPreviewKeyDown(object sender, PreviewKeyDownEventArgs previewKeyDownEventArgs)
{
if (!_textboxEnable) textBox1.Visible = true;
if (!textBox1.Focused) textBox1.Focus();
}
}
Any reason you can't just pass the event onto the "child control" ? Below example is KeyPress but the same idea applies for KeyDown
//Parent Control Visible
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
richTextBox1_KeyPress(sender, e);
}
//Child Control Hidden
private void richTextBox1_KeyPress(object sender, KeyPressEventArgs e)
{
richTextBox1.Text += e.KeyChar.ToString();
}
You can make a custom control which inherites the a textbox. YOu can place that custom control instead. In that custom control you can write a method which calls it:
public class MyTextBox : TextBox
{
public void TriggerKeyPress(KeyPressEventArgs e)
{
this.OnKeyPress(e);
}
}
If you dont want to make use of custom controlls you can make an extension to a textbox and use reflection to call it:
public static class TextBoxExtensions{
public static void TriggerKeyPress(this TextBox textbox, KeyPressEventArgs e)
{
MethodInfo dynMethod = textbox.GetType().GetMethod("OnKeyPress",
BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(textbox, new object[]{ e });
}
}
But with both methods you should know: This will not add the text from one object to another. To do that you will have to write that in your code manually.
when running my game (snake) I am suppose to be able to move the snake around the form using the keys w,a,s and d. (atm I have only written the code for left and right movement just has a jumping off point). However, when running the program nothing happens. I have tried using break points, however, it seems as if my program isn't even reading the keypress method, even though I am pressing keys.
Here is the Move method in the snake class.
public void Move(int pixels)
{
if (pixels < 0)
{
xPosition_ = xPosition_ -= SNAKE_WIDTH;
}
else if (pixels > 0)
{
xPosition_ = xPosition_ += SNAKE_WIDTH;
}
}
And here is the keypress method.
private void GameScreen_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 'a')
{
snake.Move(-1);
}
if (e.KeyChar == 'd')
{
snake.Move(1);
}
this.Refresh();
}
the graphics get drawn fine to the pictureBox control.
Thanks in advance.
One possible problem is that your event handler isn't actually connected to the KeyPress event. You need to wire up the KeyPress event to your event handler in order to make it work; simply naming it GameScreen_KeyPress isn't enough. For example, here's how you could do this in the constructor of GameScreen:
public void GameScreen()
{
this.KeyPress += new EventHandler(GameScreen_KeyPress);
}
Here's an MSDN article on the subject: How to Consume Events in a Windows Forms Application.
The best way of doing this is to override ProcesCmdKey for your form, like the example below:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
MessageBox.Show("Pressed: " + keyData);
return base.ProcessCmdKey(ref msg, keyData);
}
Do you have the KeyPreview set to true on the form?
If I do not create an "Edit->Copy" menu item and assign it the shortcut keys "CTRL+C", then I can select a control (RichTextBox, DataGridView, etc..) and hit "CTRL+C" and the control itself will handle the copy. I can copy text out, and paste it into notepad, etc..
Now throughout my whole form, I have a lot of controls. But I have a custom control that I want to make clear that I handle Copy functionality for. So I added the ShortcutKey CTRL+C to Edit->Copy, and by default it is set to Enabled.
Now, I have to implement an event handler for the 'click' event on that menu item. If I explicitly put in code to handle the copy, then it works:
public void menuEditCopy_Click(object sender, EventArgs e)
{
myCustomControl.Copy();
}
However, now Copy does not work on any other type of control. My first inclination was to find out the type of control that has focus, and implement a limited set of copy code for each of them:
public void menuEditCopy_Click(object sender, EventArgs e)
{
if (this.ActiveControl is MyCustomControl)
{
((MyCustomControl)this.ActiveControl).Copy();
}
else if (this.ActiveControl is RichTextBox)
{
((RichTextBox)this.ActiveControl).Copy();
}
}
etc...
However, my controls are added to a SplitContainer, and debugging shows that this.ActiveControl is set to the splitcontainer instance, not the control, even though I know that control is selected.
So my last thought is to literally check if every control has focus:
public void menuEditCopy_Click(object sender, EventArgs e)
{
if (myCustomControl.Focused)
{
myCustomControl.Copy();
}
else if (richTextBox1.Focused)
{
richTextBox1.Copy();
}
}
I would like to avoid this if possible, it is a lot of controls, and if I add a new control, I would need to update it. Is there a better way of doing this?
Thanks
A SplitContainer implements ContainerControl, so you could check for either one and look for it's ActiveControl instead. ContainerControl is the base class, so I would go for that - you might catch another type of container as well:
private void DoCopy(Control control)
{
if(control is ContainerControl)
DoCopy(control.SelectedControl);
else if(control is MyCustomControl)
((MyCustomControl)control).Copy();
else if(control is RichTextBox)
((RichTextBox)control).Copy();
else
throw new NotSupportedException("The selected control can't copy!");
}
void menuEditCopy_Click(object sender, EventArgs e)
{
DoCopy(this.ActiveControl);
}
You could try settting the KeyPreview property of your form to true. Then you could set up a handler for the form's KeyDown event which would look like the following:
private void Form_KeyDown(object sender, KeyEventArgs e)
{
if(e.Modifiers == Keys.Control && e.KeyCode == Keys.C)
{
if (ActiveControl.GetType() == typeof(MyCustomControl))
{
((MyCustomControl)ActiveControl).Copy();
e.Handled = true;
}
}
}
Here you are specifying that you have handled the Ctrl-C event by setting the event args Handled property to true. Else, if you leave it as false, the Ctrl-C key press will be handled as per usual by each individual control.
Because we have set the KeyPreview to true the form's handler gets to see each key press before any other control that it contains and can decide to deal with the key press itself or else allow it to be handled in the same way as if the form had never previewed it.
I think as well it would be necessary to remove the short-cut key from your menu item (although you could still manually put the text "Ctrl+C" next to your menu item name) for this to work, otherwise your menu item will hijack the key stroke.
My ActiveX control contains various shapes which are drawn. CTRL-A is used in the control to select all the objects. Similarly CTRL-C to copy, CTRL-V to paste etc.
However, when I insert this control within a Windows form in a .Net application, it does not receive these keyboard events. I tried adding a PreviewKey event, and this does allow certain keystrokes to be sent e.g. TAB, but not these modified keys.
Does anybody know how to redirect modified keystrokes to a user control?
Thanks.
It's possible that the ActiveX control doesn't have focus and is therefore not receiving the key events. You may want to handle the key events at the form level and then call the appropriate methods on your ActiveX control. If you set the KeyPreview property of your form to true your form will receive the key events for all controls on the form. That way, your shortcuts should work no matter what control currently has focus. Here is a quick example you can play with to test this out. Create a new form with several different controls on it and modify the code like so:
public Form1()
{
InitializeComponent();
KeyPreview = true; // indicates that key events for controls on the form
// should be registered with the form
KeyUp += new KeyEventHandler(Form1_KeyUp);
}
void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.Modifiers == Keys.Control)
{
switch (e.KeyCode)
{
case Keys.A:
MessageBox.Show("Ctrl + A was pressed!");
// activeXControl.SelectAll();
break;
case Keys.C:
MessageBox.Show("Ctrl + C was pressed!");
// activeXControl.Copy();
break;
case Keys.V:
MessageBox.Show("Ctrl + V was pressed!");
// activeXControl.Paste();
break;
}
}
}
No matter what control has focus when you enter the key combinations, your form's Form1_KeyUp method will be called to handle it.
You need to trap the keys and override the ProcessCmdKey method.
class MyDataGrid : System.Windows.Forms.DataGrid
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
...........
}
}
http://support.microsoft.com/kb/320584
KeyPreview is just the wrong method. Try using KeyUp or KeyDown, like this:
private void ControlKeyTestForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.A)
this.label1.Text = "Ctrl+A pressed";
}
If you want the containing form to deal with shortcut keys remember to set the KeyPreview property on the form to true then set the KeyDown or KeyUp handlers in the form.
Use Control.ModifierKeys Property to check for Modifier keys.
For example, to check for shift key,
try if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) { }
Full example here:
http://msdn.microsoft.com/en-us/library/aa984219%28VS.71%29.aspx