WPF中设置窗口位置跟随光标遇到技术问题
解决WPF高DPI下GetCursorPos坐标偏移问题
嘿,太懂你这种头疼了——高DPI笔记本(120%缩放)下用P/Invoke的GetCursorPos()拿的坐标和实际光标位置对不上,本质就是物理像素和WPF的设备无关单位(DIPs)不匹配闹的!咱来一步步搞定它:
问题根源
WPF默认以96DPI(100%缩放)作为基准单位,而你的系统是120DPI(120%缩放),物理像素和DIP的比例是1.2:1。GetCursorPos()返回的是屏幕物理像素坐标,直接用在WPF里自然会有偏移。
方案1:完善你的像素转单位方法
先把需要的P/Invoke声明补全,再优化转换逻辑:
// 先声明必要的P/Invoke函数和结构体 [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetCursorPos(out POINT lpPoint); [DllImport("user32.dll")] private static extern IntPtr GetDC(IntPtr hWnd); [DllImport("gdi32.dll")] private static extern int GetDeviceCaps(IntPtr hdc, int nIndex); [DllImport("user32.dll")] private static extern int ReleaseDC(IntPtr hWnd, IntPtr hdc); private const int LOGPIXELSX = 88; // 水平DPI参数 private const int LOGPIXELSY = 90; // 垂直DPI参数 [StructLayout(LayoutKind.Sequential)] private struct POINT { public int X; public int Y; } // 完善后的像素转WPF单位方法 private Point ConvertPixelsToUnits(int x, int y) { IntPtr dDC = GetDC(IntPtr.Zero); if (dDC == IntPtr.Zero) return new Point(x, y); // 获取当前系统的DPI值 int dpiX = GetDeviceCaps(dDC, LOGPIXELSX); int dpiY = GetDeviceCaps(dDC, LOGPIXELSY); ReleaseDC(IntPtr.Zero, dDC); // 计算缩放比例:当前DPI / WPF基准DPI(96) double scaleX = dpiX / 96.0; double scaleY = dpiY / 96.0; // 把物理像素转换为WPF的DIP单位 return new Point(x / scaleX, y / scaleY); }
调用的时候这样用:
POINT cursorPos; if (GetCursorPos(out cursorPos)) { Point wpfCursorPos = ConvertPixelsToUnits(cursorPos.X, cursorPos.Y); // 现在wpfCursorPos就是WPF界面里的正确坐标啦 }
方案2:用WPF自带API更省心
其实WPF已经帮我们封装了DPI转换逻辑,不用手动算DPI,直接用PresentationSource来转换:
private Point GetWpfCursorPosition() { POINT cursorPos; if (!GetCursorPos(out cursorPos)) return new Point(); // 获取当前应用主窗口的PresentationSource(也可以传你需要的目标元素) PresentationSource source = PresentationSource.FromVisual(Application.Current.MainWindow); if (source == null) return new Point(); // 一键转换物理像素到WPF单位 Point wpfPos = source.CompositionTarget.TransformFromDevice.Transform(new Point(cursorPos.X, cursorPos.Y)); return wpfPos; }
这个方法的好处是:自动适配多显示器的不同DPI缩放,比手动计算更可靠。
额外提醒:拖放事件里的快捷方式
如果是在WPF的拖放事件(比如DragEnter/Drop)里,其实直接用DragEventArgs.GetPosition()就好——这个方法已经自动处理了DPI缩放,根本不需要调用GetCursorPos():
private void OnDrop(object sender, DragEventArgs e) { // 获取相对于目标元素的正确坐标 Point dropPos = e.GetPosition((UIElement)sender); }
内容的提问来源于stack exchange,提问作者dansan
相关产品推荐
相关产品推荐

