WPF:忽略覆盖层/装饰器上的鼠标点击,但处理MouseEnter事件

22
我真正想要的是一个忽略鼠标“点击”事件但仍能捕获鼠标“进入和离开”事件的IsHitTestVisible版本。
背景: 每当控件获取焦点时,都会弹出一种信息覆盖层。这是一个需求,因此我无法取消此行为。这是使用包含填充有图像刷的矩形形状的附加装饰器实现的。所有控件都是以编程方式创建的,没有涉及XAML。
期望的行为: 当用户将鼠标悬停在矩形上方时,它应该变得部分透明。这样他们就可以看到覆盖层下面的其他控件并单击它们。 当用户单击覆盖层时,单击应传递给位于覆盖层下方的任何控件,在用户单击的位置下方。
问题: 如果我将IsHitTestVisible设置为True以允许鼠标单击通过,则不会收到MouseEnter事件。
是否有一种简单的方法可以将IsHitTestVisible保留为True,然后将除2-3个事件之外的所有事件传递到附加装饰器下面的正确控件?我正在寻找一种解决方案,不需要计算鼠标下方的控件,因为WPF显然可以代替我进行此操作。
或者,我可以将IsHitTestVisible设置为False,然后使用另一种简单的方法来确定鼠标是否在附加装饰器上方?
更新:我仍然希望得到答案,但截至目前,最有希望的解决方案似乎是将IsHitTestVisible保留为true,并使用WPF命中测试API来确定鼠标光标下面的控件类型;如果它是我知道的一个,我会向它发送Click命令。不确定是否值得这样做;截至目前,单击会关闭我的覆盖层,因此用户只需单击两次。
谢谢!

你解决过这个问题吗?我有完全相同的需求。 - Grokys
1个回答

7

由于IsHitTestVisible="False"禁用了所有鼠标交互,似乎没有任何干净的方法来解决这个问题。我尝试了以下方法:

  • 在OnPreviewMouseDown中设置IsHitTestVisible="False",然后使用UIAutomation重新单击装饰器,但不起作用
  • 将Opacity绑定到Adorner下面的“不可见”元素,以便单击可以通过。它确实通过了,但是问题在下一级上是相同的,所以不起作用。

看起来唯一让其正常工作的方法就是在OnMouseDown中设置IsHitTestVisible="False",然后使用SendInput模拟新的MouseClick。虽然不太美观,但能完成任务。

protected override void OnMouseDown(MouseButtonEventArgs e)
{
    IsHitTestVisible = false;

    Action action = () =>
    {
        MouseSimulator.ClickLeftMouseButton();
        IsHitTestVisible = true;
    };
    this.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); 
}

public class MouseSimulator
{
    [DllImport("user32.dll", SetLastError = true)]
    static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    struct INPUT
    {
        public SendInputEventType type;
        public MouseKeybdhardwareInputUnion mkhi;
    }
    [StructLayout(LayoutKind.Explicit)]
    struct MouseKeybdhardwareInputUnion
    {
        [FieldOffset(0)]
        public MouseInputData mi;

        [FieldOffset(0)]
        public KEYBDINPUT ki;

        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct HARDWAREINPUT
    {
        public int uMsg;
        public short wParamL;
        public short wParamH;
    }
    struct MouseInputData
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public MouseEventFlags dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
    [Flags]
    enum MouseEventFlags : uint
    {
        MOUSEEVENTF_MOVE = 0x0001,
        MOUSEEVENTF_LEFTDOWN = 0x0002,
        MOUSEEVENTF_LEFTUP = 0x0004,
        MOUSEEVENTF_RIGHTDOWN = 0x0008,
        MOUSEEVENTF_RIGHTUP = 0x0010,
        MOUSEEVENTF_MIDDLEDOWN = 0x0020,
        MOUSEEVENTF_MIDDLEUP = 0x0040,
        MOUSEEVENTF_XDOWN = 0x0080,
        MOUSEEVENTF_XUP = 0x0100,
        MOUSEEVENTF_WHEEL = 0x0800,
        MOUSEEVENTF_VIRTUALDESK = 0x4000,
        MOUSEEVENTF_ABSOLUTE = 0x8000
    }
    enum SendInputEventType : int
    {
        InputMouse,
        InputKeyboard,
        InputHardware
    }

    public static void ClickLeftMouseButton()
    {
        INPUT mouseDownInput = new INPUT();
        mouseDownInput.type = SendInputEventType.InputMouse;
        mouseDownInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN;
        SendInput(1, ref mouseDownInput, Marshal.SizeOf(new INPUT()));

        INPUT mouseUpInput = new INPUT();
        mouseUpInput.type = SendInputEventType.InputMouse;
        mouseUpInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTUP;
        SendInput(1, ref mouseUpInput, Marshal.SizeOf(new INPUT()));
    }
}

在这个项目中,我们将严格使用 WPF,不允许使用旧的东西。但是感谢您的建议。 - stone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接