Capture all mouse events in user control - c#

I'm trying to capture all mouse events in a user control (even the ones that occur in child controls). For that I use the "override WndProc"-approach:
protected override void WndProc(ref Message m)
{
System.Diagnostics.Debug.WriteLine(m.Msg.ToString());
base.WndProc(ref m);
}
I'm especially interested in mouse events, but that does not seem to work. I do get mouse button up/down events, but I don't get mouse move and mouse wheel events.
Any ideas?

Best you could do is implement IMessageFilter in your control.
public class CustomMessageFilter:UserControl,IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
//Process your message here
throw new NotImplementedException();
}
}
you can write your message filtering logic in PreFilterMessage Method.
Then just install it to the list of Message Filter in Main method.
Application.AddMessageFilter(new CustomMessageFilter());
This is a Application level hook, that means you can control all the Win32 message within application.

The correct way to capture all mouse events in a control is to call that control's Control.Capture method.
Typically this is a temporary state like doing drag n drop, user-drawing, and so forth.

Related

Can I ensure my control catches the first event regardless of whether my form has focus?

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

About treeview control

I have used the mouse down event of the treeview control. And I want to set the selected node as the node on which the mouse down event has happened. How can this problem be resolved?
The MouseDown event is fired before the node is selected. Try handling the AfterSelect event instead. If e.Action is set to TreeViewAction.ByMouse then the event was raised by the mouse.
A couple of options come to mind here. Both might well be overkill, but they still solve the problem.
Handle the MouseDown event and use the HitTest method to determine which node the user clicked on. If they clicked on a valid node, manually set the focus to that node via the SelectedNode property.
private void myTreeView_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
myTreeView.SelectedNode = myTreeView.HitTest(e.Location).Node;
}
}
The bazooka-style solution is to override the WndProc method and listen for WM_RBUTTONDOWN messages. I've done this in my own extended version of the TreeView control because it allows me to fix some really minor stuff that normal, non-obsessive people probably wouldn't notice. I go into excruciating detail in my answer here.
Basically, you're doing the same thing as the code above does, but at a lower level, which stops the native control from pulling some shenanigans with the focus. I don't remember if they actually apply here (hence the potential overkill), but I'm too lazy to fire up Visual Studio to see for sure.
public class FixedTreeView : System.Windows.Forms.TreeView
{
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_RBUTTONDOWN = 0x204;
if (m.Msg == WM_RBUTTONDOWN)
{
Point mousePos = this.PointToClient(Control.MousePosition);
this.SelectedNode = this.GetNodeAt(mousePos);
}
base.WndProc(ref m);
}
}
The first method should work just fine for you. Try that before breaking out bigger weapons.

Event bubbling - Mousehover event for child controls

I have a winform with some controls on it.
While the cursor is enters the form (form boundaries, this includes form's content) I want to trigger specific task.
When cursor leavs the form, i want to trigger another task.
Obviously seeting these events on form won't work: (because, for instance MouseLeave will bw triggered when i move from form itself to other control)
this.MouseEnter += (sender, e) =>
{
//do stuff
};
this.MouseLeave += (sender, e) =>
{
//do stuff
};
I laso tried IMessageFilter interface as appears in similar questions, but none gives me the needed result!
The problem is to detect when mouse leave the form completely.
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEMOVE: //or other messages
bool z = myForm.Bounds.Contains(Cursor.Position); //This didn't help
}
eturn false;
}
There is also problem with detection within the form content (when i move between controls).
What am I missing?
update: i don't want to use timers!
You could get the current x,y coordinates of the pointer and see if they are within the form. If they aren't, then perform the off-form task.
I pulled a trick like this once to keep tabs on all forms' ancestors:
Forms have a ControlAdded event. You could hook yourself into that (probably before InitializeForm. Inside that event handler can hook yourself into the ControlAdded and MouseXXX events of all children and grandchildren. You could send all the events of the same type to the same handler.
With that methodology, I suppose that the easiest solution will then be to watch the MouseLeave and MouseEnter events of all ancestors and then check the mouse cursor position to see if it's in the form's screen rectangle (your event handlers will be mashed full of events firing off as your mouse moves around the form). Anyway, then you'll have it solved with zero timers!

Arrow key events not arriving

Basically, I have a form with a custom control on it (and nothing else). The custom control is completely empty, and the form has KeyPreview set to true.
With this setup, I am not receiving any KeyDown events for any arrow keys or Tab. Every other key that I have on my keyboard works. I have KeyDown event handlers hooked up to everything that has such events, so I'm sure I'm not missing anything.
Also of note is that if I remove the (completely empty) custom control, I DO get the arrow key events.
What on earth is going on here?
EDIT:
I added this to both the form and the control, but I'm STILL not getting arrow keys:
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x100: //WM_KEYDOWN
//this is the control's version. In the form, it's this.Text
ParentForm.Text = ((Keys)m.WParam).ToString();
break;
}
base.WndProc(ref m);
}
I also checked with Spy++, and determined that the form itself is not getting any WM_KEYDOWN messages, they're all going to the control. However, that said, the control IS getting the arrow key WM_KEYDOWN messages. Sigh.
Edit 2: I've also updated the ZIP file with this version. Please look at it, if you want to help...
Edit 3:
I've figured this out, sort of. The form is eating the arrow keys, probably in an attempt to maintain focus amongst its children. This is proven by the fact that I DO get the events if the form is empty.
Anyway, if I add this code to the form, I start getting the events again:
public override bool PreProcessMessage(ref Message msg) {
switch (msg.Msg) {
case 0x100: //WM_KEYDOWN
return false;
}
return base.PreProcessMessage(ref msg);
}
When I override this, the form doesn't get a chance to do its dirty work, and so I get my KeyDown events as I expect. I assume that a side effect of this is that I can no longer use my keyboard to navigate the form (not a big deal in this case, as it's a game, and the entire purpose of this exercise is to implement keyboard navigation!)
The question still remains about how to disable this "properly", if there is a way...
I've done some extensive testing, and I've figured everything out. I wrote a blog post detailing the solution.
In short, you want to override the ProcessDialogKey method in the form:
protected override bool ProcessDialogKey(Keys keyData) {
return false;
}
This will cause the arrow keys (and tab) to be delivered as normal KeyDown events. HOWEVER! This will also cause the normal dialogue key functionality (using Tab to navigate controls, etc) to fail. If you want to retain that, but still get the KeyDown event, use this instead:
protected override bool ProcessDialogKey(Keys keyData) {
OnKeyDown(new KeyEventArgs(keyData));
return base.ProcessDialogKey(keyData);
}
This will deliver a KeyDown message, while still doing normal dialogue navigation.
If focus is your issue, and you can't get your user control to take a focus and keep it, a simple work-around solution would be to echo the event to your user control on the key event you are concerned about. Subscribe your forms keydown or keypress events and then have that event raise an event to your user control.
So essentially, Form1_KeyPress would Call UserControl1_KeyPress with the sender and event args from Form1_KeyPress e.g.
protected void Form1_KeyPress(object sender, KeyEventArgs e)
{
UserControl1_KeyPress(sender, e);
}
Otherwise, you may have to take the long route and override your WndProc events to get the functionality you desire.

How to pass messages from a child user-control to the parent

This is a Windows Forms / .Net C# question.
I have a borderless windows whose transparency key and background color make it completely transparent. Inside the window are a couple of user controls.
I want to be able to move the window. I know how to do this on the parent window, but my problem is that the child controls are the only thing visible and thus the only thing click-able.
The question is: how can I pass certain messages up to the Parent so the Parent can move when the right mouse button is down and the mouse is moving on any one of the child controls?
Or maybe you can suggest another way?
Thanks for the help.
Mark
You can achieve your goal even without SendMessage using System.Windows.Forms.Message class. If you have done dragging I guess you are familiar with WM_NCLBUTTONDOWN message. Send it to you parent from your control's MouseDown event.
Here is an example for moving the form clicking on control label1. Note the first line where sender is used to release the capture from clicked control. This way you can set this handler to all controls intended to move your form.
This is complete code to move the form. Nothing else is needed.
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
private void label1_MouseDown(object sender, MouseEventArgs e)
{
(sender as Control).Capture = false;
Message msg = Message.Create(Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
base.WndProc(ref msg);
}
Hope this helps.
I think the easiest way is to add this event to your child controls:
/// <summary>
/// The event that you will throw when the mouse hover the control while being clicked
/// </summary>
public event EventHandler MouseRightClickedAndHoverChildControl;
After, all the parent have to do is to subscribe to those events and make the operations to move the Parent:
ChildControl.MouseRightClickedAndHoverChildControl += OnMouseHoverChildControl;
private void OnMouseHoverChildControl(object sender, EventArgs e)
{
//do foo...
}
You need to call the SendMessage API function to send mouse messages to your parent control.
It would probably be easiest to do this by overriding your control's WndProc method.
I had exactly this question... but came up with a different answer.
If you have the Message in your WndProc, you can just change the handle to your Parent's handle and then pass it along.
I needed to do this in our derived TextBox... TextBox eats scroll wheel events even when its ScrollBars are set to None. I wanted those to propagate on up to the Form. So, I simply put this inside the WndProc for my derived TextBox:
case 0x020A: // WM_MOUSEWHEEL
case 0x020E: // WM_MOUSEHWHEEL
if (this.ScrollBars == ScrollBars.None && this.Parent != null)
m.HWnd = this.Parent.Handle; // forward this to your parent
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;

Categories

Resources