Can I make WPF set IsMouseOver for both the covering and covered element?

后端 未结 3 1507
刺人心
刺人心 2021-01-21 01:20

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

相关标签:
3条回答
  • 2021-01-21 01:40

    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....
    
    0 讨论(0)
  • 2021-01-21 01:49

    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);
    }
    
    0 讨论(0)
  • 2021-01-21 02:02

    Use IsMouseDirectlyOver property. It seems to be thing you need.

    http://msdn.microsoft.com/en-us/library/system.windows.uielement.ismousedirectlyoverproperty.aspx

    0 讨论(0)
提交回复
热议问题