I've read quite some articles now about key press events but I can't figure out how to get them. I know that only the current control with keyboard focus gets the press events. But how can i ensure that my user control has it?
Tried this without luck:
public partial class Editor : UserControl
this.SetStyle(ControlStyles.Selectable, true);
this.TabStop = true;
...
//take focus on click
protected override void OnMouseDown(MouseEventArgs e)
{
this.Focus();
base.OnMouseDown(e);
}
...
protected override bool IsInputKey(Keys keyData)
{
return true;
}
And also this:
//register global keyboard event handlers
private void Editor_ParentChanged(object sender, EventArgs e)
{
if (TopLevelControl is Form)
{
(TopLevelControl as Form).KeyPreview = true;
TopLevelControl.KeyDown += FCanvas_KeyDown;
TopLevelControl.KeyUp += FCanvas_KeyUp;
TopLevelControl.KeyPress += FCanvas_KeyPress;
}
}
The latter gave me the key down and up events, but still no key press. Is there any other method i can use to just get every down/up/press events when my control inherits from UserControl?
Edit:
As there was a comment linking another SO question: It's important that I also get the KeyPress event, since it sends the correct character on every keyboard no matter which language. This is important for text writing. If you only get the individual keys you have to process them into the correct character on your own. But maybe there is a convenience method to transform the pressed keys into a localized character?
Set your parent form's (contains the usercontrol) KeyPreview property to true
Add a new KeyPress event to your parent form
Set the parent form keypress event to forward the event to your usercontrol:
private void parentForm_KeyPress(object sender, KeyPressEventArgs e)
{
// Forward the sender and arguments to your usercontrol's method
this.yourUserControl.yourUserControl_KeyPress(sender, e);
}
Replace the yourUserControl1_KeyPress method with your own method, which you want to run each time the user presses a button (the button is pressed down and then released).
You can also create a new KeyPress handler to your usercontrol, and forward the sender and KeyPressEventArgs objects there, as in this example.
Can you attach to form event.
Private Sub MyControl_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim f = Me.FindForm
If Not f.KeyPreview Then Throw New Exception("Form requires has Keypreview enabled")
AddHandler f.KeyUp, Sub(sender2 As Object, e2 As KeyEventArgs)
End Sub
End Sub
Related
I have a Form1_KeyDown event for form1, but while I am typing some text in the Textbox on the Form, the event gets triggered.
How can I stop that event when I am typing inside the textboxes on the form.
There are two ways:
Set form's KeyPreview to false. Which is the default, so you must have explicitly changed it, presumably for a reason. Otherwise the controls of on the form always get the keyboard events first.
Add an active control check in Form1_KeyDown, like this:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (this.ActiveControl == textBox1) return;
var k = e.KeyCode;
}
I just started programming, and I want to use WinForms to make multiple buttons that you can click on to change from white to lime-green and back to white. I have done this for one button:
private void button1_Click(object sender, EventArgs e)
{
if (button1.BackColor != Color.Lime)
{
button1.BackColor = Color.Lime;
}
else
{
button1.BackColor = Color.White;
}
}
Now I could copy and paste that for all of the buttons, but I know that is inefficient; and if I use winforms to reference button1 on button2, it will just change the color of button1 (obviously).
So, do I need to use a helper method, new class, or something else? What would that look like?
There are a couple of approaches. One might be to create a common function which the different buttons call:
private void button1_Click(object sender, EventArgs e)
{
ChangeColor(button1);
}
private void ChangeColor(Button button)
{
if (button.BackColor != Color.Lime)
button.BackColor = Color.Lime;
else
button.BackColor = Color.White;
}
Then each button handler can use that same function call.
Or, if all of these buttons will always ever do exactly the same thing, then you can use one click handler function for all of them. In this case what you'd need to do is determine which button invoked the handler (whereas you're currently referencing button1 directly) so that you know which one to change. The sender object passed into the handler function is actually a reference to the form element which invoked the handler. All you need to do is cast it:
private void button_Click(object sender, EventArgs e)
{
var button = (Button)sender;
if (button.BackColor != Color.Lime)
button.BackColor = Color.Lime;
else
button.BackColor = Color.White;
}
So first the handler grabs a reference to the button which invoked it, then runs the logic on that button. Note also how I made the name of the handler function slightly more generic. Now you'd go to the form designer and set button_Click as the click handler for all of the buttons which should invoke this.
You do this the exact same way you'd do it for any C# class. You derive your own class and customize the base class behavior. Every event has a corresponding OnXxxx() method that you can override.
Add a new class to your project and paste this code:
using System;
using System.Windows.Forms;
class MyButton : Button {
protected override void OnClick(EventArgs e) {
// Your code here
//...
base.OnClick(e);
}
}
Change the code in OnClick() to do what you want to do. Compile. You'll now have your own button control on the top of the toolbox. And can drop as many copies of it as you want on a form. They'll all behave the same without having to add any code in the form.
Probably the easiest way would be to have each button invoke the same click handler. Then inside of your handler use the Sender instead of hard coding Button1.
private void buttons_Click(object sender, EventArgs e)
{
var theButton = (Button) sender;
if (theButton.BackColor != Color.Lime)
{
theButton.BackColor = Color.Lime;
}
else
{
theButton.BackColor = Color.White;
}
}
You can get the button that raised the Click event by casting sender to Button.
You can then add the same handler to every button.
I'm a VB guy.... in VB.Net you can add multiple handlers for events and connect multiple events to the same handler.
This sub hooks all clicks to color the buttons.
Private Sub ColorButtons(sender As System.Object, e As System.EventArgs) _
Handles Button1.Click, Button2.Click, ..
I do this all the time accidentally because I drag/copy a control to make a new one and the new button gets added to the original's events.
Other Subs can handle the same events to do other work - both will execute.
No idea how to do this in C#.
The proper way to do this really is to associate each button's click event to the function you have coded for that purpose (you want the function to run when the button is clicked, right?), so add the following (or similar) to an appropriate section of your code:
MyButton1.Click += new RoutedEventHandler(buttons_Click);
MyButton2.Click += new RoutedEventHandler(buttons_Click);
etc...
You can associate as many controls to the event handler as you like.
What I usually do before is this:
private void button2_Click(object sender, EventArgs e)
{
button1.PerformClick();
}
This code will just simply run the codes under button1_Click.
But try not to practice as such and just simply put it in a function/method just like what David suggested.
I know this has been asked hundred of times, but I haven't been able to find a solution that helps me. I'm using a barcode scanner and I want to be able to get the keys that are typed using just the keydown events. For some reason, I can't use both keydown and keypress events (my keypress events won't run).
I need to be able to get the characters, including hyphens, uppercase letters and dots and also need to detect the enter key.
These are my listeners:
form.KeyDown += new KeyEventHandler(Input_KeyDown);
form.KeyPress += new KeyPressEventHandler(Input_KeyPress);
And these are my methods:
private void TimedOut(object sender, EventArgs e)
{
_barcode = "";
}
private void Input_KeyDown(object sender, KeyEventArgs e)
{
_timer.Stop();
_timer.Start();
if (e.KeyData == Keys.Enter)
{
if (!_barcode.Equals(""))
{
this.BarcodeScanned(_barcode, new EventArgs());
}
}
else
{
}
}
private void Input_KeyPress(object sender, KeyPressEventArgs e)
{
_timer.Stop();
_timer.Start();
_barcode += e.KeyChar;
}
Your code above works...on a blank form. However there are several things that can interfere with the key events, especially when there are other controls on the page. Make sure that
The AcceptButton property isn't set on the form (this will trap the Enter key)
That there are no controls on the form with TabStop set to true (might not be viable but give it a go)
That the form has focus when you're typing (unlikely given the description but check anyway)
That focus is not otherwise on any control in the form when typing, e.g., a TextBox
That no other controls are trying to process the KeyPress or KeyDown events and that no other custom events are configured/set anywhere else in your code
One thing I notice is that you are registering the events like so;
form.KeyDown += new KeyEventHandler(Input_KeyDown);
This implies that you are instantiating this form from another place and trying to get it to send its key events to the calling code. Are you sure that the form instance is persisted/saved to a private class level variable or some such?
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.
I have a c# form (let's call it MainForm) with a number of custom controls on it. I'd like to have the MainForm.OnClick() method fire anytime someone clicks on the form regardless of whether the click happened on the form or if the click was on one of the custom controls. I'm looking for behavior similar to the KeyPreview feature of forms except for mouse clicks rather than key presses.
I recommend creating a base form for the other forms in your application to inherit. Add this code to your base form to create a new event called GlobalMouseClickEventHandler:
namespace Temp
{
public delegate void GlobalMouseClickEventHander(object sender, MouseEventArgs e);
public partial class TestForm : Form
{
[Category("Action")]
[Description("Fires when any control on the form is clicked.")]
public event GlobalMouseClickEventHander GlobalMouseClick;
public TestForm()
{
InitializeComponent();
BindControlMouseClicks(this);
}
private void BindControlMouseClicks(Control con)
{
con.MouseClick += delegate(object sender, MouseEventArgs e)
{
TriggerMouseClicked(sender, e);
};
// bind to controls already added
foreach (Control i in con.Controls)
{
BindControlMouseClicks(i);
}
// bind to controls added in the future
con.ControlAdded += delegate(object sender, ControlEventArgs e)
{
BindControlMouseClicks(e.Control);
};
}
private void TriggerMouseClicked(object sender, MouseEventArgs e)
{
if (GlobalMouseClick != null)
{
GlobalMouseClick(sender, e);
}
}
}
}
This solution will work not only for top-level controls, but also nested controls such as controls placed inside of panels.
In the form's ControlAdded event, add a MouseClick handler to the control, with the Address of the form's click event. I haven't tested this, but it might work.
Private Sub Example_ControlAdded(ByVal sender As Object, ByVal e As System.Windows.Forms.ControlEventArgs) Handles Me.ControlAdded
AddHandler e.Control.MouseClick, AddressOf Example_MouseClick
End Sub
Private Sub Example_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
MessageBox.Show("Click")
End Sub
The only way I've ever managed to do this is to handle the [c]Click[/c] event of every control. I don't believe the event is raised before the control processes it.
In WPF, there are "tunneling" preview events that provide this functionality, but that doesn't really help you in WinForms.
You can hook all the control's events, if you like, and then monitor that way. I assume there is some uber fancy Win32 api way to trap them all, but that is beyond me at the moment.
public Form1()
{
InitializeComponent();
HookEvents();
}
private void HookEvents() {
foreach (Control ctl in this.Controls) {
ctl.MouseClick += new MouseEventHandler(Form1_MouseClick);
}
}
void Form1_MouseClick(object sender, MouseEventArgs e)
{
LogEvent(sender, "MouseClick");
}
// and then this just logs to a multiline textbox you have somwhere on the form
private void LogEvent(object sender, string msg) {
this.textBox1.Text = string.Format("{0} {1} ({2}) \n {3}",
DateTime.Now.TimeOfDay.ToString(),
msg,
sender.GetType().Name,
textBox1.Text
);
}
The output is something like this, showing all the events and who "sent" them up:
14:51:42.3381985 MouseClick (Form1)
14:51:40.6194485 MouseClick (RichTextBox)
14:51:40.0100735 MouseClick (TextBox)
14:51:39.6194485 MouseClick (Form1)
14:51:39.2131985 MouseClick (RichTextBox)
14:51:38.8694485 MouseClick (Button)
HTH.
Catching a click on an open space on the form is easy, but to get a click that's actually on a control, you'll need the cooperation of that control to send it to the form.
One possibility is to place a transparent control over the entire form, and accept clicks onto that, deal with them, and then pass them onto the proper control underneath.
This is a common pattern in development, its called the Observer pattern. There are lots of examples of Observer patterns and c# here is 1 example http://msdn.microsoft.com/en-us/library/ms954621.aspx but have a good google around.