Winforms event when a mouse leaves a control - c#

I have a user control that's overlaid over other controls. A button brings it up and I want it to hide (Visible = false) when the mouse leaves it. What event should I use? I tried Leave, but that only fires after I manually hide it. I also thought about MouseLeave, but that's fired never.
EDIT: The control consists of a ListView and a Panel with a bunch of buttons in it. They are docked straight in the control, with no top-level container.

UserControl is in fact a panel holding some controls on it for convenience and easy re-use (it has advantage of design time support). In fact when you move your mouse out of the UserControl, one of its child control fires the MouseLeave, not the UserControl itself. I think you have to implement some Application-wide MouseLeave for your UserControl like this:
public partial class YourUserControl : UserControl, IMessageFilter {
public YourUserControl(){
InitializeComponent();
Application.AddMessageFilter(this);
}
bool entered;
public bool PreFilterMessage(ref Message m) {
if (m.Msg == 0x2a3 && entered) return true;//discard the default MouseLeave inside
if (m.Msg == 0x200) {
Control c = Control.FromHandle(m.HWnd);
if (Contains(c) || c == this) {
if (!entered) {
OnMouseEnter(EventArgs.Empty);
entered = true;
}
} else if (entered) {
OnMouseLeave(EventArgs.Empty);
entered = false;
}
}
return false;
}
}

Related

How to prevent context menu from closing when holding the Alt key?

I need to allow the Alt key to be used as a modifier when selecting certain menu entries in a ContextMenuStrip. This works fine with all other modifiers like Ctrl or Shift. However, for some reason Alt automatically closes the context menu.
I tried using the Closing event in the context menu, and the menu item AutoClose property, but this is proving to be more clunky than anticipated. Specifically, even though the context menu is kept open, the application MenuStrip is activated (which may explain why the context menu closes in the first place).
I've tried searching around but I found surprisingly few questions about this, and none on stack overflow, so I wonder whether there might be a better way to work around this that I have missed?
Example code showing that suppressing MenuStrip activation does not prevent context menu from closing:
class MainForm : Form
{
MenuStrip menuStrip;
ContextMenuStrip contextMenuStrip;
public MainForm()
{
KeyPreview = true;
menuStrip = new MenuStrip();
menuStrip.Items.Add("&File");
Controls.Add(menuStrip);
contextMenuStrip = new ContextMenuStrip();
contextMenuStrip.Items.Add("&OptionA");
contextMenuStrip.Items.Add("&OptionB");
ContextMenuStrip = contextMenuStrip;
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.Alt)
{
e.Handled = true;
e.SuppressKeyPress = true;
}
else base.OnKeyDown(e);
}
}
This is by design, so you'll have to do the state tracking yourself. But this will definitely stop Alt from reaching the MenuStrip.
This is low level keyboard filtering, so you'll have to decide what to do when Alt is pressed entirely on your own, however.
You could also change the conditional to check for Alt plus some state.
In short, returning true from PreFilterMesssage will stop it from reaching your app.
static void Main()
{
//...SNIP...
Application.AddMessageFilter(new AltFilter());
//...SNIP...
}
public class AltFilter : IMessageFilter
{
private static ushort WM_SYSKEYDOWN = 0x0104;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_SYSKEYDOWN && Control.ModifierKeys == Keys.Alt)
{
//Do your own special thing instead
return true;
}
return false;
}
}

Focus on scroll

I have a user control with a scrollbar (scrollbar appears as a contained user control, which inherits from Panel, is too large). When using the mouse to scroll all is well, but trying to scroll with the mousewheel dont work.
My solution here is to set focus to my child-control in an eventhandler for Scroll. This works. Now the question; Will this result in a lot of unecessary calls to childControl.Focus()? Is there a more neat way of doing this?
Edit: I think I was a bit unclear with my question so Rephrasing the question:
is
private void ChildControl_OnScroll(object sender, ScrollEventArgs scrollEventArgs)
{
this.childControl.Focus();
}
a bad way of setting the focus? I.e. will the focus be set mutliple times each time I scroll? or rather, will this cause (tiny) performance issues.
Here's another approach that gives focus when the scrollbar area of panel1 inside SomeUserControl is clicked. It uses NativeWindow so you don't have to change the panel in your UserControl. This way Focus() will only be called once, when the mouse goes down in the scrollbar area:
public partial class SomeUserControl : UserControl
{
private TrapMouseDownOnScrollArea trapScroll = null;
public SomeUserControl()
{
InitializeComponent();
this.VisibleChanged += new EventHandler(SomeUserControl_VisibleChanged);
}
void SomeUserControl_VisibleChanged(object sender, EventArgs e)
{
if (this.Visible && trapScroll == null)
{
trapScroll = new TrapMouseDownOnScrollArea(this.panel1);
}
}
private class TrapMouseDownOnScrollArea : NativeWindow
{
private Control control = null;
private const int WM_NCLBUTTONDOWN = 0xA1;
public TrapMouseDownOnScrollArea(Control ctl)
{
if (ctl != null && ctl.IsHandleCreated)
{
this.control = ctl;
this.AssignHandle(ctl.Handle);
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_NCLBUTTONDOWN:
if (this.control != null)
{
Rectangle screenBounds = control.RectangleToScreen(new Rectangle(0, 0, control.Width, control.Height));
if (screenBounds.Contains(Cursor.Position))
{
control.Focus();
}
}
break;
}
base.WndProc(ref m);
}
}
}
This might be overkill for your scenario, but it demonstrates one way to trap lower level messages. As said before, you could also derive from Panel to achieve the same affect. You could also trap messages at the application level with IMessageFilter.
The MouseWheel event is an event that "bubbles". Windows sends it to the control that has the focus, regardless of where the mouse cursor is located. The most typical problem is that you have a control that cannot receive the focus. A Panel for example.
This changes when you put a control on the panel. Now that control can get the focus and gets the MouseWheel message. It won't have any use for it so the message passes to its parent. Which does have a use for it, the panel scrolls as expected.
You can get a focusable panel control from this answer. A generic "make it work like a browser or Office program" solution from this question
If childControl has a MouseEnter() event then use that instead:
private void childControl_MouseEnter(object sender, EventArgs e)
{
childControl.Focus();
}
Then the mouse wheel events should be direct to childControl.

detect mouse click in parent form, but outside child form

i am trying to implement a UI whereby the parent form loads a child form through showDialog(). the child form would be closed whenever i click anywhere outside the child form, but inside the parent form. clicking anywhere outside the parent form would only cause a normal "alt-tab" action. how do i do this?
If you don't have any controls in the form (if you're viewing a picture for example). Then you can just capture the mouse:
protected override void OnLoad( EventArgs e )
{
base.OnLoad( e );
this.Capture = true;
}
And after that, you just check in OnMouseDown if the click is outside your form.
Othewise, this code could be used:
protected override void WndProc(ref Message m)
{
if ( m.Msg==0x86 && (int)m.WParam==0 )
if ( this.DialogResult==DialogResult.None )
this.DialogResult = DialogResult.OK;
base.WndProc (ref m);
}
It worked great in Windows XP, but in Windows 7 it sounds a beep too, and I haven't investigated why.
I needed the same behavior.
When my application starts, an advertising form is shown and has to be closed whenever the user click on the main form.
Based on the WM_SETCURSOR message, here my solution (to put in the main form) :
protected override void WndProc(ref Message m)
{
var vanishingDialog = ActiveForm as IVanishingDialog;
//0x0201FFFE is for : 0201 (left button down) and FFFE (HTERROR).
if ((m.Msg == 0x20) && (m.LParam.ToInt32() == 0x0201FFFE) && (vanishingDialog != null))
{
vanishingDialog.Vanish();
}
else
{
base.WndProc(ref m);
}
}
And my Advertising dialog (or whatever you want to see disappear on a click to the main form) will implement this interface :
public interface IVanishingDialog
{
/// <summary>
/// Closes the dialog box.
/// </summary>
void Vanish();
}
Work like a charm on seven (no beep).
I just need to improve it in one way :
When the user click on a button in the main form, the advertising form close but the button is not pressed.
I have to transform and send a new message.

How to handle Form caption right click

I'd like a context menu on the caption bar right click
any tips/samples pref in c# ?
UPDATE - for various reasons, right click on the form won't work because the form is not empty and the form is composited dynamically so....
You can do this by trapping the WM_NCRBUTTONDOWN notification that Windows sends when the user right-clicks the title bar. The control class does not have an event for it, you'll need to override WndProc(). Here's an example form, you'll need to add a ContextMenuStrip:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
protected void OnTitlebarClick(Point pos) {
contextMenuStrip1.Show(pos);
}
protected override void WndProc(ref Message m) {
const int WM_NCRBUTTONDOWN = 0xa4;
if (m.Msg == WM_NCRBUTTONDOWN) {
var pos = new Point(m.LParam.ToInt32());
OnTitlebarClick(pos);
return;
}
base.WndProc(ref m);
}
}
MSDN explains how to handle right-clicks on Windows Forms controls. Controls, including Forms, inherit the MouseClick event.
MouseEventArgs will tell you what button was clicked through the Button property. Have a look at the MouseButtons Enumeration.
if you handle the form mouse-click, you can then use the following code:
private void Dialog_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
this.Text = "new caption text";
}
}
But you'll have to make sure that you generate this event for the top-level control on a form. For instance if you have a group box on the form, it will receive the mouse-click events rather than the form itself, for the areas of the form that are under the group box.
There is already a menu managed by Windows when you right-click the titlebar.
Do you want to replace it completely?
If you want to add to it you will have to use the Win32 API and interop and you will have to subclass the form.
See the AppendMenu() function.
Basically you need to use p-invoke to do this. There is a really great example at Here
You can see from the example you will need to manually mimic the event handlers, but this is pretty straight forward.
You can override WndProc of the form and capture the WM_NCRBUTTONDOWN message:
protected override void WndProc(ref Message m)
{
const int WM_NCRBUTTONDOWN = 0xA4;
if (m.Msg == WM_NCRBUTTONDOWN)
{
MessageBox.Show("Caption right clicked!");
}
else
{
base.WndProc(ref m);
}
}
This code will suppress the window's context menu, however. You may not wish this. The WM_NCRBUTTONDOWN message will also be sent if you right click the window borders as well. You may not desire this either.

C# Tab switching in TabControl

Iam facing such problem, which I find hard to overcome. In WinForms I got a TabControl with n TabPages. I want to extend the Ctrl+Tab / Ctrl+Shift+Tab switching. so I wrote some code which works fine as long as the focus is on TabControl or on the Form. When application focus is INSIDE of TabPage (for example on a button which is placed inside of TabPage), while pressing Ctrl+Tab, my code is ignored and TabControl skips to TabPage on his own (avoiding my code).
Anyone idea ?
You need to derive from TabControl and override ProcessCmdKey, virtual method in order to override the Ctrl-Tab behavior.
Example:
public class ExtendedTabControl: TabControl
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.Tab))
{
// Write custom logic here
return true;
}
if (keyData == (Keys.Control | Keys.Shift | Keys.Tab))
{
// Write custom logic here, for backward switching
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
TabControl has fairly unusual processing to handle the Tab key. It overrides the ProcessKeyPreview() method to detect Ctrl/Shift/Tab, then implements the tab selection in its OnKeyDown() method. It does this so it can detect the keystroke both when it has the focus itself as well as any child control. And to avoid stepping on custom Tab key processing by one of its child controls. You can make it work by overriding ProcessCmdKey() but then you'll break child controls that want to respond to tabs.
Best thing to do is to override its OnKeyDown() method. Add a new class to your project and paste the code shown below. Compile. Drop the new tab control from the top of the toolbox onto your form.
using System;
using System.Windows.Forms;
class MyTabControl : TabControl {
protected override void OnKeyDown(KeyEventArgs e) {
if (e.KeyCode == Keys.Tab && (e.KeyData & Keys.Control) != Keys.None) {
bool forward = (e.KeyData & Keys.Shift) == Keys.None;
// Do your stuff
//...
}
else base.OnKeyDown(e);
}
}
Beware that you also ought to consider Ctrl+PageUp and Ctrl+PageDown.

Categories