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

发布于 2024-09-26 09:25:47 字数 770 浏览 9 评论 0原文

我真正想要的是 IsHitTestVisible 的一个版本,它可以忽略鼠标点击事件,但仍然捕获鼠标进入和离开事件。

背景: 随时随地将信息叠加弹出在控件下。这是一项要求,因此我没有自由来消除这种行为。这是使用装有矩形形状的装饰物实现的,该矩形形状充满了图像刷。所有控件都是通过编程创建的,不涉及XAML。

期望的行为: 当用户在矩形上进行慕斯时,应部分透明。这样一来,他们就可以看到覆盖层下面的其他控件并单击它们。 当用户点击覆盖层时,应将单击传递到覆盖下的任何控件,就在用户点击的位置。

问题: 如果我将 Ishittestvisible设置为true,以允许鼠标点击通过,我不会得到鼠标ententer事件。

有没有一种简单的方法可以将 IsHitTestVisible 保留为 True,然后将除 2-3 个事件之外的所有事件传递给装饰器下方的正确控件?我正在寻找一种不涉及计算光标下方的控件的解决方案,因为 WPF 显然能够为我执行此操作。

或者,我可以将 IsHitTestVisible 设置为 False 但然后使用另一种简单的方法来确定鼠标何时位于装饰器上?

更新:我仍然希望得到答案,但到目前为止,最有希望的解决方案似乎是让 IsHitTestVisible 为 true,并使用 WPF 命中测试 API 来找出该控件下面的控件类型。鼠标光标;如果这是我知道的,我会向它发送一个 Click 命令。但不确定这是否值得做;截至目前,单击将删除我的覆盖层,因此用户只需单击两次即可。

谢谢!

What I really want is a version of IsHitTestVisible that ignores mouse click events but still traps mouse enter and leave events.

Background:
An informational overlay pops up under the control with focus whenever. This is a requirement, so I'm not at liberty to remove this behavior. This is implemented using an adorner containing a Rectangle shape, filled with an image brush. All controls are created programatically, no XAML involved.

Desired behavior:
When user mouses over the Rectangle, it should become partially transparent. This is so that they can see the other controls beneath the overlay and click them.
When the user clicks on the overlay, the click should be passed through to whatever control is under the overlay, right where the user clicked.

Problem:
If I set IsHitTestVisible to True to allow mouse clicks to pass through, I don't get MouseEnter events.

Is there a simple way to leave IsHitTestVisible True, and then pass all but 2-3 events to the correct control beneath the adorner? I'm looking for a solution that does not involve calculating what control is beneath the cursor, since WPF is clearly capable of doing this for me.

Alternatively, could I set IsHitTestVisible to False but then use another simple method to determine when the mouse is over the adorner?

UPDATE: I'm still hoping for an answer, but as of now the most promising solution seems to be leaving IsHitTestVisible true, and using the WPF hit testing APIs to figure out what type of control was underneath the mouse cursor; if it was one I know about, I'd send a Click command to it. Not sure if this is worth doing, though; as of now clicking dismisses my overlay so the user just has to click twice.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

擦肩而过的背影 2024-10-03 09:25:47

由于IshittestVisible =“ false”禁用所有鼠标的交互,因此似乎并不是任何干净的方法。我尝试

  • 在 OnPreviewMouseDown 中设置 IsHitTestVisible="False" ,然后使用 UIAutomation 重新单击装饰器,但没有将不
  • 透明度绑定到装饰器下的“不可见”元素,以便单击可以通过。它通过了一切顺利,但是当然,问题在下一个层面上是相同的,因此没有骰子。

似乎真正让它起作用的唯一方法是在 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()));
    }
}

Since IsHitTestVisible="False" disables all mouse interaction, it doesn't seem to be any clean way of doing this. I tried

  • Setting IsHitTestVisible="False" in OnPreviewMouseDown and then re-clicking the adorner using UIAutomation but no dice
  • Binding the Opacity to an "invisible" element under the Adorner so that the click would pass through. It passed through all right, but of course the problem was the same on the next level so no dice.

Seems like the only way to actually get this to work is to set IsHitTestVisible="False" in OnMouseDown and then simulate a new MouseClick using SendInput. Not very pretty but it gets the job done.

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()));
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文