为啥 WPF 中的鼠标位置不正确,而缩放桌面上的 Winforms 则不正确?

Posted

技术标签:

【中文标题】为啥 WPF 中的鼠标位置不正确,而缩放桌面上的 Winforms 则不正确?【英文标题】:Why is mouse position incorrect in WPF and not Winforms on scaled desktops?为什么 WPF 中的鼠标位置不正确,而缩放桌面上的 Winforms 则不正确? 【发布时间】:2021-09-04 17:24:33 【问题描述】:

我正在制作一个“吸管”工具,可让您在鼠标光标下选择/查看屏幕上任何位置的颜色。我想在窗口上显示颜色信息,并让 WPF 窗口在您移动时跟随光标。

颜色部分很好。实际上,我在让窗口跟随光标移动时遇到了最大的麻烦。鼠标数据完​​全不正确。

Winforms 中的相同代码可以完美运行。

我尝试将 app.manifests 添加到两者中,以使每个项目都可以识别 DPI。这似乎对 WPF 项目没有影响。

使用 .NET 5 和 C#。目前在 4k 显示器上进行了 150% 的测试。

这是我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Windows.Threading;

namespace WpfApp1

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetCursorPos(out POINT lpPoint);

        DispatcherTimer timer = new DispatcherTimer();

        public struct POINT
        
            public int X;
            public int Y;


            public static implicit operator Point(POINT point)
            
                return new Point(point.X, point.Y);
            
        

        public MainWindow()
        
            InitializeComponent();

            timer.Interval = new TimeSpan(15);
            timer.Start();
            timer.Tick += Timer_Tick;
        

        private void Timer_Tick(object sender, EventArgs e)
        
            POINT p;
            GetCursorPos(out p);
            this.Left = p.X;
            this.Top = p.Y;
        
    

【问题讨论】:

请注意,new TimeSpan(15) 创建的计时器间隔为 15 个“滴答”,即 1.5 微秒。当然不是你想要的。 您是否尝试处理WM_MOUSEMOVE 事件而不是使用计时器来轮询光标位置? 【参考方案1】:

查看这个帖子:How to configure an app to run correctly on a machine with a high DPI setting (e.g. 150%)?

如果您选择手动检查缩放因子,您可以查询显示变换矩阵:

Matrix? matrix = PresentationSource.FromVisual(visual)?.CompositionTarget?.TransformToDevice;

visual 是 System.Windows.Media.Visual 对象 - 您的主窗口。 属性M11M22 包含水平和垂直缩放因子(通常是相同的值)。使用这些比例因子来计算实际的鼠标位置。

如果您正在处理多显示器设置,请注意您获得的是哪个屏幕的缩放系数。

还有一个可以帮助你的帖子:C# - How to get real screen resolution in multiple monitors context?

【讨论】:

以上是关于为啥 WPF 中的鼠标位置不正确,而缩放桌面上的 Winforms 则不正确?的主要内容,如果未能解决你的问题,请参考以下文章

在WPF里面实现以鼠标位置为中心缩放移动图片

WPF中获取鼠标相对于桌面位置

计算在鼠标光标位置放大的视图偏移

相对于鼠标位置从其中心缩放图像

OpenGL/GLFW:为啥将电视用作笔记本电脑的主显示器时检测到的鼠标位置不正确?

鼠标滚轮时的oxyplot轴锁定中心