using System;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
namespace NFission.WinForms
{
public sealed class HighlightFilter : System.Windows.Forms.IMessageFilter
{
#region static add / remove monitor functionality
private static HighlightFilter instance;
private static Pen highlightPen;
///
/// The System.Color to use when highlighting controls
///
public static Color HighlightColor
{
get{ return highlightPen.Color;}
set
{
highlightPen.Color = value;
instance.HighlightControl(instance.lastControl);
}
}
///
/// The width of the line used to highlight controls
///
public static float HighlightWidth
{
get{ return highlightPen.Width;}
set
{
highlightPen.Width = value;
instance.HighlightControl(instance.lastControl);
}
}
///
/// Monitors all controls on a form, and highlights controls when the mouse moves over them
///
/// The form whose controls you wish to monitor
public static void MonitorForm(Form monitorForm)
{
MonitorControl((Control)monitorForm);
}
///
/// Monitors a control and all child controls, highlighting them when the mouse moves over them.
///
/// The control to monitor
public static void MonitorControl(Control monitorControl)
{
if(instance == null)
{
instance = new HighlightFilter();
Application.AddMessageFilter( instance);
}
PruneTree();
foreach(WeakReference wr in instance.monitored)
{
if(wr.Target == monitorControl)
{
//already being monitored... don't double up.
return;
}
if(((Control)wr.Target).Contains(monitorControl))
{
//a parent is already being monitored.. don't add it??
return;
}
}
WeakReference weak = new WeakReference( monitorControl);
instance.monitored.Add(weak);
}
///
/// Stops monitoring a form or control
///
/// The control or form to stop monitoring
public static void RemoveMonitor(Control monitored)
{
if(instance == null) return;
foreach(WeakReference wr in instance.monitored)
{
if(wr.Target == monitored)
{
instance.monitored.Remove(wr);
if( ((Control)wr.Target).Contains(instance.lastControl))
{
instance.RemoveHighlights();
}
break;
}
}
PruneTree();
if( instance.monitored.Count == 0 )
{
instance.Dispose();
instance = null;
}
}
private static void PruneTree()
{
WeakReference wr;
for(int i=0; i< instance.monitored.Count; i++)
{
wr = (WeakReference)instance.monitored[i];
if(! wr.IsAlive)
{
instance.monitored.Remove(wr);
i--;
}
}
}
private HighlightFilter()
{
monitored = new ArrayList();
highlightPen = new Pen(Color.Yellow, 2f);
}
#endregion
private ArrayList monitored;
private Control lastControl;
private const int TRAPMESSAGE = 0x200; //0x200 is wm_mousemove. I've also used 0x204, rbuttondown
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if( m.Msg == TRAPMESSAGE) //trap wm_mousemove, wm_rbuttondown, whatever.
{
Control ctrl = GetMonitoredControl( m.HWnd);
if(ctrl != lastControl)
{
RemoveHighlights();
}
if( ctrl != null)
{
HighlightControl(ctrl);
}
}
return false;
}
private void HighlightControl(Control child)
{
if( child != null)
{
lastControl = child;
Graphics g = child.CreateGraphics();
g.DrawRectangle( highlightPen, Rectangle.Inflate(child.ClientRectangle, -1, -1));
g.Dispose();
}
}
private void RemoveHighlights()
{
if(lastControl != null)
{
//cheating is easy!
lastControl.Invalidate();
}
}
#region Find the applicable control, if any
//Given an hWnd, return a Control object that
//should be highlighted. If no monitored control
//matches the hWnd, return null
private Control GetMonitoredControl(IntPtr hWnd)
{
Control ctrl = Control.FromHandle(hWnd);
foreach( WeakReference wr in monitored)
{
if(wr.IsAlive)
{
Control parent = (Control)wr.Target;
if( !parent.IsDisposed)
{
if(parent.Contains(ctrl))
return ctrl;
if(parent == ctrl && !(parent is Form))
return ctrl;
}
}
}
return null;
}
#endregion
#region Disposing of resources
//note that this type does NOT implement IDisposable, as
// .. no code should ever have an instance of the class.
// Any resemblance to IDisposable.Dispose is coincidental
private bool isDisposed = false ;
private void Dispose()
{
if(!isDisposed)
{
isDisposed = true;
Application.RemoveMessageFilter(this);
highlightPen.Dispose();
}
}
#endregion
}
}