【问题标题】:Releasing mouse capture and letting mouse click pass through释放鼠标捕获并让鼠标单击通过
【发布时间】:2011-08-10 18:12:30
【问题描述】:

我有一个类似于弹出窗口或菜单的控件。我想显示它,当用户在框外单击时,让它隐藏起来。我已经使用了 Mouse.Capture(this, CaptureMode.SubTree) 并以与 OnLostMouseCapture 中的 Menu/Popup 相同的方式重新获取了捕获。

当用户在控件边界之外单击时,我在 OnPreviewMouseDown 中释放鼠标捕获。我没有将 e.Handled 设置为 true。鼠标单击将使其到达主 UI 上的其他控件,但不会到达窗口的关闭按钮(红色 X)。需要点击 2 次才能关闭应用。

有没有办法告诉 WPF 重新启动鼠标单击,或者发送重复的鼠标单击事件?

这是我的代码。请注意,我将其重命名为 MainMenuControl - 我没有构建菜单,因此 Menu/MenuItem 和 Popup 不是选项。

public class MainMenuControl : Control
    {
        static MainMenuControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MainMenuControl), new FrameworkPropertyMetadata(typeof(MainMenuControl)));
        }

        public MainMenuControl()
        {
            this.Loaded += new RoutedEventHandler(MainMenuControl_Loaded);

            Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnPreviewMouseDownOutsideCapturedElementHandler);
        }

        void MainMenuControl_Loaded(object sender, RoutedEventArgs e)
        {
            this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(MainMenuControl_IsVisibleChanged);
        }

        void MainMenuControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (this.IsVisible)
            {
                Mouse.Capture(this, CaptureMode.SubTree);
                Debug.WriteLine("Mouse.Capture");
            }
        }

        // I was doing this in OnPreviewMouseDown, but changing to this didn't have any effect
        private void OnPreviewMouseDownOutsideCapturedElementHandler(object sender, MouseButtonEventArgs e)
        {
            Debug.WriteLine("OnPreviewMouseDownOutsideCapturedElementHandler");

            if (!this.IsMouseInBounds())
            {
                if (Mouse.Captured == this)
                {
                    Mouse.Capture(this, CaptureMode.None);
                    Debug.WriteLine("Mouse.Capture released");
                }
                Debug.WriteLine("Close Menu");
            }
        }

        protected override void OnLostMouseCapture(MouseEventArgs e)
        {
            base.OnLostMouseCapture(e);

            Debug.WriteLine("OnLostMouseCapture");

            MainMenuControl reference = e.Source as MainMenuControl;
            if (Mouse.Captured != reference)
            {
                if (e.OriginalSource == reference)
                {
                    if ((Mouse.Captured == null) || (!reference.IsAncestorOf(Mouse.Captured as DependencyObject)))
                    {
                        //TODO: Close
                        Debug.WriteLine("Close Menu");
                    }
                }
                // if a child caused use to lose the capture, then recapture.
                else if (reference.IsAncestorOf(e.OriginalSource as DependencyObject))
                {
                    if (Mouse.Captured == null)
                    {
                        Mouse.Capture(reference, CaptureMode.SubTree);
                        Debug.WriteLine("Mouse.Capture");
                        e.Handled = true;
                    }
                }
                else
                {
                    //TODO: Close
                    Debug.WriteLine("Close Menu");
                }
            }

        }

        private bool IsMouseInBounds()
        {
            Point point = Mouse.GetPosition(this);
            Rect bounds = new Rect(0, 0, this.Width, this.Height);

            return bounds.Contains(point);
        }

    }

【问题讨论】:

    标签: wpf popup mouse capture


    【解决方案1】:

    问题在于您所讨论的鼠标处理在 WPF 事件系统之外并且是操作系统的一部分,因此我们实际上是在讨论两个完全不同的鼠标消息队列,它们在大多数情况下交互得很好,但在这些边缘情况,我们看到互操作性并不完美。

    您可以尝试生成 Win32 鼠标消息或向您自己的窗口发送关闭消息,但所有这些方法都是 hack。由于弹出窗口和菜单表现出与您描述的完全相同的症状,因此似乎没有一种简单的方法可以实现您所描述的内容。

    相反,我建议您考虑在鼠标离开窗口的北客户区或其他一些启发式方法(例如与控件的指定距离)时放弃鼠标捕获。我知道这可能并不理想,但如果您希望关闭按钮工作得足够糟糕,这可能是一个令人满意的折衷方案。

    【讨论】:

    • 您的建议非常有效。我把这一切都放在一个行为中——当我移出主窗口时释放捕获并订阅 App.Current.MainWindow.MouseEnter 以重新捕获鼠标。您也说得对,WPF 和 Windows 不共享相同的消息泵 - 我比较了 WPF 和非 WPF 菜单行为,并看到了点击的差异。您的解决方案提供了我想要的正确(非 WPF)行为。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2011-10-08
    • 2014-01-15
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多