You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WPF中获取控件到屏幕底部的距离(含缩放、多屏场景)

解决WPF中控件到屏幕底部距离的计算问题

我之前在处理多屏幕+高DPI缩放场景时,也踩过类似的坐标计算坑,给你梳理几个可行的解决思路:

核心问题分析

你遇到的问题本质是两个关键点:

  1. 单位不匹配PointToScreen()返回的是物理像素坐标,而SystemParameters系列属性返回的是WPF默认的设备无关单位(1/96英寸),当屏幕缩放(比如175%)时,两者直接计算会出现数值偏差。
  2. 多屏幕未定位:你调用的PrimaryScreenHeight等都是主屏幕的参数,如果控件在第二块屏幕上,用主屏幕的尺寸计算肯定不对。

解决方案一:借助WinForms获取控件所在屏幕(简单快捷)

这种方式需要添加对System.Windows.Forms的引用,代码简洁易读:

  1. 先获取控件的屏幕物理坐标:
Point controlScreenPoint = yourTargetControl.PointToScreen(new Point(0, 0));
  1. 定位该坐标所在的屏幕:
var targetScreen = System.Windows.Forms.Screen.FromPoint(
    new System.Drawing.Point((int)controlScreenPoint.X, (int)controlScreenPoint.Y)
);
  1. 计算到屏幕底部的距离:
// 到屏幕最底部(包含任务栏等区域)
double distanceToScreenBottom = targetScreen.Bounds.Bottom - controlScreenPoint.Y;

// 到工作区底部(不含任务栏)
double distanceToWorkAreaBottom = targetScreen.WorkingArea.Bottom - controlScreenPoint.Y;

解决方案二:纯WPF+P/Invoke实现(无需WinForms引用)

如果不想依赖WinForms,可以通过调用Windows API来获取屏幕信息:

首先定义所需的P/Invoke结构体和方法:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;

public static class ScreenHelper
{
    [DllImport("user32.dll")]
    private static extern IntPtr MonitorFromPoint(POINT pt, uint dwFlags);

    [DllImport("user32.dll")]
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int X;
        public int Y;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct MONITORINFO
    {
        public int cbSize;
        public RECT rcMonitor;
        public RECT rcWork;
        public uint dwFlags;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    public static double GetDistanceToScreenBottom(Visual visual)
    {
        Point screenPoint = visual.PointToScreen(new Point(0, 0));
        POINT pt = new POINT { X = (int)screenPoint.X, Y = (int)screenPoint.Y };
        // 获取离该点最近的屏幕
        IntPtr hMonitor = MonitorFromPoint(pt, 0x00000002); 

        MONITORINFO monitorInfo = new MONITORINFO();
        monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);
        GetMonitorInfo(hMonitor, ref monitorInfo);

        return monitorInfo.rcMonitor.Bottom - screenPoint.Y;
    }
}

然后直接调用即可:

double distance = ScreenHelper.GetDistanceToScreenBottom(yourTargetControl);

额外提示:验证DPI缩放的影响

如果你需要手动转换单位,可以通过VisualTreeHelper.GetDpi()获取控件的缩放因子,把设备无关单位转换成物理像素:

var dpi = VisualTreeHelper.GetDpi(yourTargetControl);
// 设备无关单位转物理像素
double physicalHeight = SystemParameters.PrimaryScreenHeight * dpi.DpiScaleY;

不过这种方式还是没解决多屏幕的问题,所以更推荐前面两种直接定位目标屏幕的方法。

内容的提问来源于stack exchange,提问作者Neil B

火山引擎 最新活动