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

后端 未结 3 1505
刺人心
刺人心 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 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....
    

提交回复
热议问题