The simplified example this; picture a Venn diagram made from two elements, A and B, that overlap. If I mouse over (A AND (NOT B)) all of A lights up. If I mouse over (B AND (NO
I needed something similiar in my project, and whipped up a quick solution.
public sealed class IsMouseOverEnchancementBehavior
{
#region MouseCurrentPosition
internal sealed class MouseCurrentPosition
{
private Point _currentPosition;
private readonly Timer _timer = new Timer(250);
public event EventHandler PositionChanged;
public MouseCurrentPosition()
{
_timer.Elapsed += timer_Elapsed;
_timer.Start();
}
public Point CurrentPosition
{
get { return _currentPosition; }
set
{
if (_currentPosition != value)
{
_currentPosition = value;
var pos = PositionChanged;
if (pos != null)
PositionChanged(null, null);
}
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref NativePoint pt);
public static Point GetCurrentMousePosition()
{
var nativePoint = new NativePoint();
GetCursorPos(ref nativePoint);
return new Point(nativePoint.X, nativePoint.Y);
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Point current = GetCurrentMousePosition();
CurrentPosition = current;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NativePoint
{
public int X;
public int Y;
};
}
#endregion
private static readonly MouseCurrentPosition _mouseCurrentPosition = new MouseCurrentPosition();
public static DependencyProperty IsMouseOverIgnoreChild =
DependencyProperty.RegisterAttached("IsMouseOverIgnoreChild", typeof (bool),
typeof (IsMouseOverEnchancementBehavior),
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsMouseOverEnchancementEnabled =
DependencyProperty.RegisterAttached("IsMouseOverEnchancementEnabled",
typeof (bool), typeof (IsMouseOverEnchancementBehavior),
new UIPropertyMetadata(false, OnMouseOverEnchancementEnabled));
private static void OnMouseOverEnchancementEnabled(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// todo: unhook if necessary.
var frameworkElement = (FrameworkElement) d;
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(UIElement.IsMouseOverProperty,
typeof (UIElement));
Action calculateCurrentStateAction = () =>
{
// cheap check.
if (frameworkElement.IsMouseOver)
{
SetIsMouseOverIgnoreChild(frameworkElement, true);
return;
}
// through hit-testing.
var isMouseOver = VisualTreeHelper.
HitTest(frameworkElement, Mouse.GetPosition(frameworkElement)) != null;
SetIsMouseOverIgnoreChild(frameworkElement, isMouseOver);
};
// if the mose moves,
// we shall re-do hit testing.
_mouseCurrentPosition.PositionChanged += delegate
{
frameworkElement.Dispatcher.Invoke(
calculateCurrentStateAction);
};
// If IsMouseOver changes,
// we can propagate it to our property.
dpd.AddValueChanged(frameworkElement,
delegate { calculateCurrentStateAction(); });
}
#region Misc
public static bool GetIsMouseOverEnchancementEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(IsMouseOverEnchancementEnabled);
}
public static void SetIsMouseOverEnchancementEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsMouseOverEnchancementEnabled, value);
}
public static bool GetIsMouseOverIgnoreChild(DependencyObject obj)
{
return (bool) obj.GetValue(IsMouseOverIgnoreChild);
}
public static void SetIsMouseOverIgnoreChild(DependencyObject obj, bool value)
{
obj.SetValue(IsMouseOverIgnoreChild, value);
}
#endregion
}
It's more of a generic solution that uses timers.
This is how I use it in the style:
...do stuff here....