wpf popup点击外部后自动关闭 自定义popup关闭逻辑

公共逻辑


        private bool IsTouchWithinPopup(TouchPoint touchPoint, Popup popup)
        {
            if (popup.Child is FrameworkElement child)
            {
                // 获取Popup子元素的边界矩形
                Rect bounds = new Rect(0, 0, child.ActualWidth, child.ActualHeight);

                // 将触摸点坐标转换为相对于子元素的坐标
                Point relativePoint = touchPoint.Position;

                // 判断坐标是否在边界内
                return bounds.Contains(relativePoint);
            }
            return false;
        }

        private bool IsTouchWithinPopup(Point touchPoint, Popup popup)
        {
            if (popup.Child is FrameworkElement child)
            {
                // 获取Popup子元素的边界矩形
                Rect bounds = new Rect(0, 0, child.ActualWidth, child.ActualHeight);

                // 将触摸点坐标转换为相对于子元素的坐标
                Point relativePoint = touchPoint;

                // 判断坐标是否在边界内
                return bounds.Contains(relativePoint);
            }
            return false;
        }


        private bool IsTouchWithinBtn(Point touchPoint, Button popup)
        {
            if (popup is FrameworkElement child)
            {
                // 获取Popup子元素的边界矩形
                Rect bounds = new Rect(0, 0, child.ActualWidth, child.ActualHeight);

                // 将触摸点坐标转换为相对于子元素的坐标
                Point relativePoint = touchPoint;

                // 判断坐标是否在边界内
                return bounds.Contains(relativePoint);
            }
            return false;
        }
        private bool IsTouchWithinBtn(TouchPoint touchPoint, Button popup)
        {
            if (popup is FrameworkElement child)
            {
                // 获取Popup子元素的边界矩形
                Rect bounds = new Rect(0, 0, child.ActualWidth, child.ActualHeight);

                // 将触摸点坐标转换为相对于子元素的坐标
                Point relativePoint = touchPoint.Position;

                // 判断坐标是否在边界内
                return bounds.Contains(relativePoint);
            }
            return false;
        }

鼠标点击逻辑

也可以实现打开popup时点击某些控件关闭popup,点击特殊的控件时不关闭popup。参考下面代码中的exceptBtnList

protected override void OnActivated(EventArgs e)
{
    var popList = new List<Popup>() { popIsFirstPage, popIsLastPage, popIsFirstQuestion, popIsLastQuestion,
                popExit, popSelectPen,  popSelectErase, popClass, popWrongQuestion };
    var exceptBtnList = new List<Button> { btnWrongQuestion, btnhignPreqWrongQuestion };
    base.OnActivated(e);
    InputManager.Current.PreProcessInput += (s, args) =>
    {
        if (args.StagingItem.Input is MouseEventArgs mouseEvent &&
            mouseEvent.RoutedEvent == UIElement.PreviewMouseDownEvent)
        {
            foreach (var item in popList)
            {
                if (item.IsOpen)
                {
                    // 判断是否点击在 Popup 外部
                    if (!IsTouchWithinPopup(mouseEvent.GetPosition(item.Child), item))
                    {
                        bool inBtn = false;
                        foreach (var btn in exceptBtnList)
                        {
                            inBtn = IsTouchWithinBtn(mouseEvent.GetPosition(btn), btn);
                            if (inBtn)
                            {
                                break;
                            }
                        }
                        if (inBtn)
                        {
                            continue;
                        }
                        item.IsOpen = false;
                    }
                }
            }
        }
    };
}

触摸逻辑

通过InputManager.Current.PreProcessInput在触摸事件执行前检测popup状态
如果点击在Popup外部则关闭

    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);
        InputManager.Current.PreProcessInput += (s, args) =>
        {
            if (args.StagingItem.Input is TouchEventArgs touchEvent &&
                touchEvent.RoutedEvent == UIElement.PreviewTouchDownEvent)
            {
                var popList = new List<Popup>() { popIsFirstPage, popIsLastPage, popIsFirstQuestion, popIsLastQuestion,
                    popExit, popSelectPen,  popSelectErase, popClass, popWrongQuestion };
                foreach (var item in popList)
                {
                    if (item.IsOpen)
                    {
                        // 判断是否点击在 Popup 外部
                        if (!IsTouchWithinPopup(touchEvent.GetTouchPoint(item.Child), item))
                        {
                            item.IsOpen = false;
                        }
                    }
                }
            }
        };
    }
     private bool IsTouchWithinPopup(TouchPoint touchPoint, Popup popup)
     {
         if (popup.Child is FrameworkElement child)
         {
             // 获取Popup子元素的边界矩形
             Rect bounds = new Rect(0, 0, child.ActualWidth, child.ActualHeight);

             // 将触摸点坐标转换为相对于子元素的坐标
             Point relativePoint = touchPoint.Position;

             // 判断坐标是否在边界内
             return bounds.Contains(relativePoint);
         }
         return false;
     }

扩展

如果需要在其他界面中点击同时保证原来的窗口中的popup仍然是打开状态,就需要监控触发点击事件中的窗口句柄是否正确


        // 添加Windows API声明
        internal static class NativeMethods
        {
            [DllImport("user32.dll")]
            public static extern IntPtr GetActiveWindow();

            [DllImport("user32.dll")]
            public static extern IntPtr WindowFromPoint(POINT point);

            [DllImport("user32.dll")]
            public static extern bool IsChild(IntPtr hWndParent, IntPtr hWnd);

            [StructLayout(LayoutKind.Sequential)]
            public struct POINT
            {
                public int X;
                public int Y;

                public POINT(int x, int y)
                {
                    X = x;
                    Y = y;
                }

                public static implicit operator Point(POINT p)
                {
                    return new Point(p.X, p.Y);
                }

                public static implicit operator POINT(Point p)
                {
                    return new POINT((int)p.X, (int)p.Y);
                }
            }
        }



//添加句柄检测
 protected override void OnActivated(EventArgs e)
 {
     var popList = new List<Popup>() { popIsFirstPage, popIsLastPage, popIsFirstQuestion, popIsLastQuestion,
                 popExit, popSelectPen,  popSelectErase, popClass, popWrongQuestion };
     var exceptBtnList = new List<Button> { btnWrongQuestion, btnhignPreqWrongQuestion };
     base.OnActivated(e);

     // 获取当前窗口句柄
     IntPtr hWnd = new WindowInteropHelper(this).Handle;

     InputManager.Current.PreProcessInput += (s, args) =>
     {
         // 获取当前活动窗口句柄
         IntPtr activeHwnd = NativeMethods.GetActiveWindow();

         // 如果活动窗口不是当前窗口,不处理
         if (activeHwnd != hWnd)
         {
             return;
         }

         if (args.StagingItem.Input is MouseEventArgs mouseEvent &&
             mouseEvent.RoutedEvent == UIElement.PreviewMouseDownEvent)
         {
             foreach (var item in popList)
             {
                 if (item.IsOpen)
                 {
                     // 判断是否点击在 Popup 外部
                     if (!IsTouchWithinPopup(mouseEvent.GetPosition(item.Child), item))
                     {
                         bool inBtn = false;
                         foreach (var btn in exceptBtnList)
                         {
                             inBtn = IsTouchWithinBtn(mouseEvent.GetPosition(btn), btn);
                             if (inBtn)
                             {
                                 break;
                             }
                         }
                         if (inBtn)
                         {
                             continue;
                         }
                         item.IsOpen = false;
                     }
                 }
             }
         }

         if (args.StagingItem.Input is TouchEventArgs touchEvent &&
             touchEvent.RoutedEvent == UIElement.PreviewTouchDownEvent)
         {
             foreach (var item in popList)
             {
                 if (item.IsOpen)
                 {
                     // 判断是否点击在 Popup 外部
                     if (!IsTouchWithinPopup(touchEvent.GetTouchPoint(item.Child), item))
                     {
                         bool inBtn = false;
                         foreach (var btn in exceptBtnList)
                         {
                             inBtn = IsTouchWithinBtn(touchEvent.GetTouchPoint(btn), btn);
                             if (inBtn)
                             {
                                 break;
                             }
                         }
                         if (inBtn)
                         {
                             continue;
                         }
                         item.IsOpen = false;
                     }
                 }
             }
         }
     };
 }
posted @ 2025-03-25 19:58  Hey,Coder!  阅读(204)  评论(0)    收藏  举报