必须在Windows Forms应用且目标框架为.NET Framework 4.8.1以下或.NET 7+ Windows桌面环境中使用Screen.AllScreens,它返回Screen[]数组,含Bounds、WorkingArea、Primary等属性,应在Form.Load或Shown事件中读取,定位窗体到指定屏幕左上角需用Bounds.Location并设StartPosition为Manual。

如何用 Screen.AllScreens 获取所有显示器信息
必须先确认当前运行环境是 Windows Forms 应用(System.Windows.Forms 命名空间可用),且目标框架为 .NET Framework 4.8.1 以下,或 .NET 7+ 的 Windows 桌面应用。否则 Screen 类不可用或行为异常。
直接调用 Screen.AllScreens 返回的是 Screen[] 数组,每个元素代表一个物理显示器,包含 Bounds(含任务栏的整个屏幕区域)、WorkingArea(排除任务栏的可用区域)、Primary(是否为主屏)等关键属性。
- 不要在窗体构造函数中读取
AllScreens,此时 UI 线程可能尚未就绪,建议放在Form.Load或Shown事件里 - 数组长度为 1 并不绝对代表单屏——某些虚拟显示器或远程桌面会伪造多屏假象,需结合
Bounds.Width * Bounds.Height和实际分辨率交叉验证 -
Screen.Primary是布尔值,不是引用;Screen.PrimaryScreen才是主显示器对象,二者别混用
如何把窗体精准定位到指定屏幕的左上角
关键不是“移到副屏”,而是“移到该屏坐标系下的 (0, 0)”。因为多屏环境下,Windows 使用统一的虚拟坐标系,主屏左上角是 (0, 0),副屏左上角可能是 (1920, 0) 或 (-1920, 0),取决于系统排列方式。
正确做法是:先拿到目标 Screen 对象(比如 Screen.AllScreens[1]),再用它的 Bounds.Location 赋给窗体 Location,同时确保窗体 StartPosition 设为 FormStartPosition.Manual。
- 如果窗体仍出现在主屏,大概率是
StartPosition还是WindowsDefaultLocation,这个属性会覆盖你手动设置的Location - 用
Bounds而非WorkingArea定位左上角,否则窗体顶部会被任务栏遮挡 - 若想让窗体居中显示在某屏,用
new Point(screen.WorkingArea.X + (screen.WorkingArea.Width - this.Width) / 2, ...)
如何应对显示器热插拔导致的窗体错位
用户拔掉副屏后,原位于该屏的窗体不会自动消失或迁移,而是卡在“已不存在”的坐标位置(比如 Location = (1920, 0)),下次启动时可能只显示标题栏边缘,甚至完全不可见。
解决方案是在窗体激活前做一次坐标校验:调用 Screen.FromPoint(this.Location),检查返回的 Screen 是否仍有效(即该点是否落在任一当前 AllScreens 的 Bounds 内)。不在则重置为默认屏。
- 校验逻辑建议放在
Form.Activated或Form.VisibleChanged中,而非仅在Load时做一次 -
Screen.FromPoint可能返回null(比如坐标超出所有屏幕范围),务必判空 - 若应用需长期驻留多屏,应监听
SystemEvents.DisplaySettingsChanged事件,主动响应分辨率/布局变更
为什么 Screen.FromControl(this) 有时返回错误屏幕
这个方法本质是找“包含控件几何中心最多”的屏幕,而不是“控件所在屏幕”。当窗体跨屏(比如一半在主屏、一半在副屏),或窗体尺寸极大覆盖多个屏幕时,结果取决于中心点落哪——容易误判。
真正可靠的依据永远是窗体自身的 Location 和 Size,配合 Screen.AllScreens 遍历比对:
var targetScreen = Screen.AllScreens.FirstOrDefault(s =>
s.Bounds.Contains(this.Location) ||
s.Bounds.IntersectsWith(new Rectangle(this.Location, this.Size)));
- 优先用
Contains判断左上角是否在某屏内,这是最稳定锚点 -
IntersectsWith适合判断窗体是否“视觉上可见于某屏”,但计算开销略高 - 避免依赖
FromControl做关键定位逻辑,它更适合辅助场景(如右键菜单适配当前屏幕 DPI)
多屏支持真正的复杂点不在获取屏幕列表,而在于坐标系是全局的、动态的、且和用户物理摆放强耦合。哪怕代码写对了,测试时也得真接两台显示器,手动拖动排列顺序,再拔一根线——模拟器很难复现热插拔瞬间的坐标偏移和 WorkingArea 缩放变化。











