Освободить захват мыши и позволить щелчку мыши пройти - PullRequest
10 голосов
/ 22 апреля 2011

У меня есть элемент управления, похожий на всплывающее окно или меню.Я хочу отобразить его, и когда пользователь щелкает за пределами поля, он скрывается.Я использовал Mouse.Capture (это, CaptureMode.SubTree), а также повторно получил захват тем же способом, что и Menu / Popup в OnLostMouseCapture.

Когда пользователь нажимает за пределами элемента управленияосвободить захват мыши в OnPreviewMouseDown.Я не установил e.Handled к истине.Щелчок мыши приведет к переходу к другим элементам управления в главном интерфейсе, но не к кнопке закрытия (красный 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);
        }

    }

1 Ответ

5 голосов
/ 22 апреля 2011

Проблема в том, что работа с мышью, о которой вы говорите, находится за пределами системы обработки событий WPF и части операционной системы, поэтому мы действительно говорим о двух довольно разных очередях сообщений мыши, которые в большинстве случаев взаимодействуют достаточно хорошо, но в этих В крайнем случае мы видим, что совместимость не идеальна.

Вы можете попытаться сгенерировать сообщения мыши для Win32 или отправить собственное окно с сообщением о закрытии, но все эти подходы - хаки. Поскольку всплывающие окна и меню демонстрируют те же симптомы, которые вы описываете, не похоже, что будет простой способ выполнить то, что вы хотите, как вы это описали.

Вместо этого я предлагаю вам отказаться от захвата мыши, когда мышь покидает северную клиентскую область окна или какую-то другую эвристику, например, на указанном расстоянии от элемента управления. Я знаю, что это, вероятно, не идеально, но это может быть удовлетворительным компромиссом, если вы хотите, чтобы кнопка закрытия работала достаточно плохо.

...