You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复WPF窗口仅吸附多显示器左上边缘的问题?

WPF多显示器窗口拖动吸附修复方案

你的代码在多显示器环境下失效的核心原因有两个:一是遍历屏幕时后续判断会覆盖之前的吸附结果,二是未处理显示器之间的相邻边缘。以下是修复后的完整代码,以及关键修改说明:

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Linq;

namespace MyTimer
{
    public partial class MainWindow
    {
        Point LastLeftClickPosition = new Point();
        int SnapThresholdInPixels = 20;

        void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.LastLeftClickPosition = e.GetPosition(this);
            Mouse.Capture(this);
        }

        void Window_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Mouse.Capture(null);
        }

        void Window_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                var currentPosition = e.GetPosition(this);
                var deltaX = currentPosition.X - this.LastLeftClickPosition.X;
                var deltaY = currentPosition.Y - this.LastLeftClickPosition.Y;

                Left += deltaX;
                Top += deltaY;

                this.SnapWindow();
            }
        }

        void SnapWindow()
        {
            var screens = System.Windows.Forms.Screen.AllScreens;
            double windowLeft = Left;
            double windowRight = Left + ActualWidth;
            double windowTop = Top;
            double windowBottom = Top + ActualHeight;

            bool snappedX = false;
            bool snappedY = false;

            // 处理单屏幕边缘吸附
            foreach (var screen in screens)
            {
                var workingArea = screen.WorkingArea;

                if (!snappedX && Math.Abs(windowLeft - workingArea.Left) < SnapThresholdInPixels)
                {
                    Left = workingArea.Left;
                    snappedX = true;
                }
                if (!snappedX && Math.Abs(windowRight - workingArea.Right) < SnapThresholdInPixels)
                {
                    Left = workingArea.Right - ActualWidth;
                    snappedX = true;
                }
                if (!snappedY && Math.Abs(windowTop - workingArea.Top) < SnapThresholdInPixels)
                {
                    Top = workingArea.Top;
                    snappedY = true;
                }
                if (!snappedY && Math.Abs(windowBottom - workingArea.Bottom) < SnapThresholdInPixels)
                {
                    Top = workingArea.Bottom - ActualHeight;
                    snappedY = true;
                }
            }

            // 处理跨屏幕垂直边缘(显示器之间的左右对齐线)
            if (!snappedX)
            {
                var verticalEdges = screens.Select(s => s.WorkingArea.Left)
                                          .Concat(screens.Select(s => s.WorkingArea.Right))
                                          .Distinct();

                foreach (var edge in verticalEdges)
                {
                    if (Math.Abs(windowLeft - edge) < SnapThresholdInPixels)
                    {
                        Left = edge;
                        snappedX = true;
                        break;
                    }
                    if (Math.Abs(windowRight - edge) < SnapThresholdInPixels)
                    {
                        Left = edge - ActualWidth;
                        snappedX = true;
                        break;
                    }
                }
            }

            // 处理跨屏幕水平边缘(显示器之间的上下对齐线)
            if (!snappedY)
            {
                var horizontalEdges = screens.Select(s => s.WorkingArea.Top)
                                            .Concat(screens.Select(s => s.WorkingArea.Bottom))
                                            .Distinct();

                foreach (var edge in horizontalEdges)
                {
                    if (Math.Abs(windowTop - edge) < SnapThresholdInPixels)
                    {
                        Top = edge;
                        snappedY = true;
                        break;
                    }
                    if (Math.Abs(windowBottom - edge) < SnapThresholdInPixels)
                    {
                        Top = edge - ActualHeight;
                        snappedY = true;
                        break;
                    }
                }
            }
        }
    }
}

关键修改说明:

  • 防止结果覆盖:新增snappedX和snappedY标记,触发单方向吸附后不再处理后续判断,避免多屏幕遍历导致的坐标被反复修改
  • 支持跨屏幕吸附:提取所有屏幕的垂直/水平边缘坐标,判断窗口边缘是否靠近这些跨屏幕中线,实现显示器之间的吸附效果
  • 优化逻辑清晰度:提前计算窗口的四个边界坐标,减少重复计算量
  • 修复语法问题:补全原代码中未闭合的方法括号

内容的提问来源于stack exchange,提问作者Joan Venge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 23:33:13