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<EventArgs> 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:
<Setter Property="behaviors:IsMouseOverEnchancementBehavior.
IsMouseOverEnchancementEnabled" Value="True" />
<Style.Triggers>
<!-- Just a visual feedback -->
<!-- Let the user know that mouse is over the element -->
<!-- When we are in editmode -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInEditMode" Value="True" />
<Condition Property="behaviors:IsMouseOverEnchancementBehavior.IsMouseOverIgnoreChild" Value="True" />
</MultiTrigger.Conditions>
...do stuff here....
You can do manual hit testing using VisualTreeHelper. This can go into a MouseMove handler on some parent object. Here I'm assuming a Venn diagram made of ellipses named RedCircle and BlueCircle:
bool overRed = false;
bool overBlue = false;
if (BlueCircle.IsMouseOver || RedCircle.IsMouseOver)
{
HitTestParameters parameters = new PointHitTestParameters(e.GetPosition(RedCircle));
VisualTreeHelper.HitTest(RedCircle, new HitTestFilterCallback(element => HitTestFilterBehavior.Continue), result =>
{
if (result.VisualHit == RedCircle)
overRed = true;
return HitTestResultBehavior.Continue;
}, parameters);
parameters = new PointHitTestParameters(e.GetPosition(BlueCircle));
VisualTreeHelper.HitTest(BlueCircle, new HitTestFilterCallback(element => HitTestFilterBehavior.Continue), result =>
{
if (result.VisualHit == BlueCircle)
overBlue = true;
return HitTestResultBehavior.Continue;
}, parameters);
}
Use IsMouseDirectlyOver property. It seems to be thing you need.
http://msdn.microsoft.com/en-us/library/system.windows.uielement.ismousedirectlyoverproperty.aspx