Created
May 18, 2017 00:30
-
-
Save franzalex/9658c101639af7da4ebd179efd560a7d to your computer and use it in GitHub Desktop.
This class allows a control to raise the MouseHover multiple times without having to move the mouse outside the control after each MouseHover event.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**************************************************************************************** | |
* Name: RepeatedHover.cs | |
* Author: Franz Alex Gaisie-Essilfie | |
* Description: Allows a control to raise the MouseHover event multiple times without | |
* having to move the mouse outside the control after each MouseHover event. | |
* | |
* Premise: From the documentation⁽¹⁾, a control's mouse events are as follows: | |
* > MouseEnter | |
* > MouseMove | |
* > MouseHover / MouseDown / MouseWheel | |
* > MouseUp | |
* > MouseLeave | |
* This makes room for the MouseHover event to be raised only once, even | |
* when the control's unobstructed DisplayRectangle is large enough to | |
* allow moving the pointer to a new location where a new MouseHover event | |
* could theoretically be raised. | |
* By using this class, the above-described constraint is bypassed, allowing | |
* a control to potentially raise the MouseHover event several times as | |
* long as the mouse pointer moves beyond the bounds after each MouseHover | |
* event has been raised. | |
* | |
* Change Log: | |
* Date | Description | |
* ------------|-------------------------------------------------------------- | |
* 2017-05-18 | Initial design | |
* | |
**************************************************************************************** | |
* ⁽¹⁾: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.mousehover.aspx#Remarks | |
**************************************************************************************** | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Drawing; | |
using System.Windows.Forms; | |
namespace WindowsFormsApp1 | |
{ | |
/// <summary> | |
/// Allows a control to raise the <see cref="Control.MouseHover">Hover</see> event multiple | |
/// times without having to move the mouse outside the control. | |
/// </summary> | |
public class RepeatedHover | |
{ | |
private HashSet<Control> hookedControls; | |
private Rectangle hoverRect; | |
private Control lastControl; | |
private Point lastLocation; | |
private Timer hoverTimer; | |
/// <summary>Occurs when the mouse pointer rests on the control.</summary> | |
public event EventHandler<MouseEventArgs> MouseHover; | |
/// <summary>Initializes a new instance of the <see cref="RepeatedHover" /> class.</summary> | |
public RepeatedHover() | |
{ | |
hookedControls = new HashSet<Control>(); | |
hoverTimer = new Timer { Interval = SystemInformation.MouseHoverTime }; | |
hoverTimer.Tick += hoverTimer_Tick; | |
} | |
/// <summary> | |
/// Gets or sets the time, in milliseconds, that the mouse pointer has to stay in the hover | |
/// rectangle before a mouse hover message is generated. | |
/// </summary> | |
/// <remarks> | |
/// <para> | |
/// The <strong>MouseHoverTime</strong> property indicates the time, in milliseconds, | |
/// that a mouse pointer must remain within an area the size of the | |
/// <see cref="MouseHoverSize" /> property in order to generate a mouse hover message. | |
/// </para> | |
/// <para> | |
/// The <see cref="MouseHoverSize" /> property indicates the size of the rectangle within | |
/// which the mouse pointer has to stay for the mouse hover time before a mouse hover | |
/// message is generated. | |
/// </para> | |
/// </remarks> | |
public int MouseHoverTime | |
{ | |
get { return hoverTimer.Interval; } | |
set { hoverTimer.Interval = value; } | |
} | |
/// <summary> | |
/// Gets or sets the dimensions, in pixels, of the rectangle within which the mouse pointer | |
/// has to stay for the mouse hover time before a mouse hover message is generated. | |
/// </summary> | |
/// <remarks> | |
/// <para> | |
/// The <strong>MouseHoverSize</strong> property indicates the size of the rectangle within | |
/// which the mouse pointer has to stay for the mouse hover time before a mouse hover | |
/// message is generated. | |
/// </para> | |
/// <para> | |
/// The <see cref="MouseHoverTime" /> property indicates the time, in milliseconds, | |
/// that a mouse pointer must remain within an area the size of the | |
/// <strong>MouseHoverSize</strong> property in order to generate a mouse hover message. | |
/// </para> | |
/// </remarks> | |
public Size MouseHoverSize { get; set; } = SystemInformation.MouseHoverSize; | |
/// <summary> | |
/// Hooks the specified control to enable repeated raising of the | |
/// <see cref="MouseHover" /> event. | |
/// </summary> | |
/// <param name="ctrl">The control to be hooked.</param> | |
public void Hook(Control ctrl) | |
{ | |
if (hookedControls.Add(ctrl)) | |
{ | |
ctrl.MouseMove += Control_MouseMove; | |
ctrl.Disposed += (o, e) => Unhook((Control)o); // to cleanly remove control on disposal | |
} | |
} | |
/// <summary> | |
/// Unhooks the specified control to stop repeated raising of the <see cref="MouseHover" /> event. | |
/// </summary> | |
/// <param name="ctrl">The control to be unhooked.</param> | |
public void Unhook(Control ctrl) | |
{ | |
if (hookedControls.Remove(ctrl)) | |
ctrl.MouseMove -= Control_MouseMove; | |
} | |
/// <summary>Raises the <see cref="E:MouseHover" /> event.</summary> | |
/// <param name="e">The <see cref="MouseEventArgs" /> instance containing the event data.</param> | |
protected void OnMouseHover(MouseEventArgs e) | |
{ | |
MouseHover?.Invoke(lastControl, e); | |
} | |
private void Control_MouseMove(object sender, MouseEventArgs e) | |
{ | |
var ctrl = (Control)sender; | |
/* | |
* Conditions for which the hover timer should be restarted: | |
* 1. The mouse is moving over a different control. | |
* 2. The mouse has moved outside the hover rectangle. | |
*/ | |
if (ctrl != lastControl || | |
!hoverRect.Contains(e.Location)) | |
{ | |
lastControl = ctrl; // update the hovered control | |
// adjust the position of the hover rectangle | |
hoverRect.Location = e.Location; | |
hoverRect.Size = Size.Empty; | |
hoverRect.Inflate(this.MouseHoverSize); | |
// restart hover timer | |
hoverTimer.Stop(); | |
hoverTimer.Start(); | |
} | |
lastLocation = e.Location; | |
} | |
private void hoverTimer_Tick(object sender, EventArgs e) | |
{ | |
OnMouseHover(new MouseEventArgs(MouseButtons.None, 0, lastLocation.X, lastLocation.Y, 0)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment