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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:08:51