Освобождение захвата мыши и пропуска мыши

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

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

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

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

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