如何修复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
相关产品推荐
相关产品推荐

