How can I capture a mouse click outside of a panel in an Windows Form application?
To capture global mouse and keyboard events you need to capture the windows messages WM_MOUSE_LL and WM_KEYBOARD_LL, not just WM_MOUSE and WM_KEYBOARD. These events can only be captured in NT/2000/XP. In later OS versions this is not possible (for fairly obvious security reasons I assume).
If you are using NT/2000/XP here is some example code:
http://www.codeproject.com/KB/cs/globalhook.aspx
The answer is in your question, set the panel's Capture property to true. All mouse input events are now directed to the panel, even if the mouse is outside of the panel window. This is however a temporary conditions (as it should be), a button click is going to cancel the capture after the click is delivered to the panel. The MouseCaptureChanged event lets you know when that happened. Unconditionally capturing the mouse is not an option, typing Ctrl+Esc for example will always cancel it.
You can handle the MouseClick event of the form itself.
class YourForm : Form
{
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
// Do something.
}
}
Related
I am a fairly new to Visual C# but have coded in Delphi for a long time. I have created a form that has a simple panel that is hidden unless you type in a particular key on the keyboard (I do have "KeyPreview" set for true for the form and I am using the KeyDown event to handle determining if the correct key was pressed and to make the panel visible/invisible). Just beneath the panel is a webBrowser component.
What is happening is as follows:
When my form initially starts, I have code for the "Shown" event that makes sure the form has focus initially:
private void Form1_Shown(object sender, EventArgs e)
{
if (!Focused)
{
Focus();
}
}
When the form is focused at this point, my code for detecting the proper keystroke to get the panel to appear or disappear works fine.
Here is where things get strange and I'm not sure of what to do. There are two parts I am dealing with for what is wrong:
If I click on another form and then on the caption bar of my form again to get focus on my form and try a keystroke, the keystroke detection does not work. However, if I click on another form and then back on my form one more time, the keystroke detection for the form does work. What can I do to make sure that this works each time my from has focus again?
If I click on the web browser component within my own form, the KeyDown code for the form no longer gets enacted. Even if I click on the caption bar for the form, the KeyDown event does not work. What do I need to do to assure that, if a component within my form is clicked, my form will still respond for the KeyDown event?
Thanks in advance for any advice.
I can't say why your caption bar seems to be intercepting key events. It may be that various components on the form can have focus and thus capture keyboard events. I know the web browser control works this way. You may consider capturing keyboard events globally.
I saw something on Codeproject that shows how to do this. I hope this helps.
They use UserActivityHooks.
UserActivityHook actHook;
void MainFormLoad(object sender, System.EventArgs e)
{
actHook= new UserActivityHook(); // crate an instance
// hang on events
actHook.OnMouseActivity+=new MouseEventHandler(MouseMoved);
actHook.KeyDown+=new KeyEventHandler(MyKeyDown);
actHook.KeyPress+=new KeyPressEventHandler(MyKeyPress);
actHook.KeyUp+=new KeyEventHandler(MyKeyUp);
}
As for the webbrowser control, of course it is going to interecept keyboard events. Users often have to enter text in forms and developers often code javascript on webpages to specifically hook into keyboard events. So the webbrowser control must be able to capture those.
I am relatively new to C#. I have a window with buttons. If the window is out of focus and I click on a button the first time, the first click grabs focus for the window and all subsequent clicks will perform their respective actions.
Is there a way to execute the event associated with the button instead of grabbing focus?
It sounds like you are describing how ToolStrips operate, which does not fire a click event unless the application has the focus.
A work around is to use your own ToolStrip and let the mouse activation give the control the focus, which in turn will then let the button fire it's click event:
public class ToolStripIgnoreFocus : ToolStrip {
private const int WM_MOUSEACTIVATE = 0x21;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_MOUSEACTIVATE && this.CanFocus && !this.Focused)
this.Focus();
base.WndProc(ref m);
}
}
Rebuild your solution and you should see a ToolStripIgnoreFocus control available in your tool box. Try adding that to your form and then add your tool buttons accordingly.
This is how most Windows apps work - the app needs to have focus before it can receive click events.
As far as I know, you've described an inherent Windows behaviour and as such it could be impossible to do.
An alternative is to harness 'Always on top' style of windows app which is explained here:
How to make a window always stay on top in .Net?
This is normal windows behavior. Something that I don't think you can override (so that the click event fires, but doesn't bring your app to the foreground, and active state).
If you don't want to bring focus to the window, but still want to provide some 'interaction' with the window itself, try keyboard hooks or hotkey events. Examples:
http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvb/thread/2622427d-1e15-4f30-b01d-57b0ba054f5c
Using global keyboard hook (WH_KEYBOARD_LL) in WPF / C#
Low-level Keyhook
http://www.pinvoke.net/default.aspx/user32/RegisterHotKey.html
I have few controls (Label, Custom Textbox, Datagridview) docked on a form. When I tried to hook the MouseMove event to the individual controls, the event fires perfectly fine but when I tried to hook the event on the form itself, the mousemove event do not respond at all. What could be the possible cause of this?
Edit:
Here is the event hook from resources.cs
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.LogicSimulationViewerForm_MouseMove);
and here is the function handled on catching the event
private void LogicSimulationViewerForm_MouseMove(object sender, MouseEventArgs e)
{
//DOESN'T WORK!!!
}
Winforms events don't bubble up like in WPF or in Html. Therefore if the controls are docked on the form, the form doesn't expose any surface of it's own and it doesn't catch any mouse events. However 'under water', all windows messages (a mouse move is a windows message), do pass the form, so it is possible to catch that message.
edit
Tigran has linked to a good example for the use of IMessageFilter that makes creating another example a bit superfluous :)
The cause of this is that in difference of WPF in WindowsForms the event is "blocked" by the control that handled it (in WPF the event will be pushed to the parent up to the Visual Tree, or in opposite direction: from parent to child).
So to catch that event you need to process it on application level and do not subscribe to single control event.
For more detailed example how to handle it can look here:
How do I capture the mouse mouse move event in my winform application
How do you get all clicks to go through the event? I noticed if you click too fast it thinks you are double clicking and doesn't send the clicks to the event handler. Is there a way to get all the clicks?
Not that sure why this question got a bounty, the accepted answer ought to be already pretty close to a solution. Except that you ought to use MouseUp instead of MouseDown, your user typically expects a click action to take effect when he releases the button. Which provides the back-out "oops, didn't mean to click it, move the mouse so it gets ignored" option.
Nevertheless, for the built-in Winforms controls, like PictureBox, this is configurable with the Control.SetStyle() method. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox:
using System;
using System.Windows.Forms;
class MyPictureBox : PictureBox {
public MyPictureBox() {
this.SetStyle(ControlStyles.StandardDoubleClick, false);
}
}
Do beware however that this won't work for the .NET classes that wrap an existing native Windows control. Like TextBox, ListBox, TreeView, etc. The underlying configuration for that is the WNDCLASSEX.style member, CS_DBLCLKS style flag. The code that sets that style flag is baked into Windows and cannot be changed. You'd need different kind of surgery to turn a double-click back into a single click. You can do so by overriding the WndProc() method, I'll give an example for TextBox:
using System;
using System.Windows.Forms;
class MyTextBox : TextBox {
protected override void WndProc(ref Message m) {
// Change WM_LBUTTONDBLCLK to WM_LBUTTONCLICK
if (m.Msg == 0x203) m.Msg = 0x201;
base.WndProc(ref m);
}
}
Just change the class name if you want to do it for other controls. Commandeering Winforms to make it work the way you want never takes much code, just Petzold :)
You want to use the controls MouseDown event instead of the Click event. MouseDown will be called every single time the mouse is "pressed" on that control. Click may not get called if the system thinks it was a double click. A DoubleClick event would be raised instead.
I think you want to count all click.
If you want to count click then set one counter variable and increase it in click event.
May be this help to you....
Is it possible to detect mouse clicks without listening to any mouse events defined in framework controls?
I mean, I don't want to write code like :
control.MouseLeftButtonDown += this.HandleMouseLeftButtonDown;
Yet I want to know if user clicks on the screen or not. Is it possible in C# (WPF or Silverlight)?
You can register a class handler in a static constructor you your main window, for example:
static MainWindow() {
EventManager.RegisterClassHandler(typeof (MainWindow),
Mouse.MouseDownEvent,
new MouseButtonEventHandler(OnGlobaMouseDown));
}
It will be a global handler for all MouseDown events.
You could use the Win32 API and detect the mouse message WM_MOUSE, something like this:
http://support.microsoft.com/kb/318804
or this example, showing use of the global mouse message WM_MOUSE_LL:
http://www.codeproject.com/KB/cs/globalhook.aspx
This is done by capturing the mouse. Which forces any mouse event to be directed to you, even if it moves outside of the window. Mouse.Capture() method.
If you need to handle mouse events of all your application, the best way is to subscribe to InputManager events.
"I mean, I don't want to write code like :
control.MouseLeftButtonDown += this.HandleMouseLeftButtonDown;"
You could always use:
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
...
}
You will need to include this though.
using System.Windows.Input;
This works for me in wpf.