首页 > 其他分享 >WPF 已知问题 开启 WM_Pointer 消息之后 获取副屏触摸数据坐标偏移

WPF 已知问题 开启 WM_Pointer 消息之后 获取副屏触摸数据坐标偏移

时间:2024-09-12 21:03:04浏览次数:7  
标签:Point 触摸 WM 坐标 WPF Pointer 屏幕

本文记录 WPF 触摸的一个已知问题,仅在开启 WM_Pointer 消息之后,将应用程序运行在包含多个屏幕的带触摸屏的设备上,如此时在非主屏幕的触摸屏上进行触摸,使用 GetStylusPoint 或 GetIntermediateTouchPoints 方法获取触摸点时,将会发现所获取的触摸点的坐标是偏的,偏的坐标差值刚好是整个屏幕距离

此问题由 少珺 小伙伴发现且修复,我只是代为记录的工具人

此问题已经报告给 WPF 官方,请看 https://github.com/dotnet/wpf/issues/8517

此问题已经被 少珺 修复,请看 https://github.com/dotnet-campus/wpf/pull/9

修复后的测试包是 https://www.nuget.org/packages/dotnetCampus.WPF.Resource/6.0.4-alpha07-test06

修复后的测试包的使用例子请参阅 https://github.com/lindexi/lindexi_gd/tree/893292f260c4570ff63e68b9e0a29052a187d0c6/BenukalliwayaChayjanehall

问题描述:

此问题发生在开启 WM_Pointer 消息之后的触摸应用程序上,此问题要求运行在多个屏幕上,且触摸到非主屏幕上。通过 GetStylusPoint 或 GetIntermediateTouchPoints 等方法获取触摸点信息时,可以看到触摸点信息存在偏差,偏差的坐标差值刚好是整个屏幕距离,也就是差了 N 个屏幕距离

复现步骤:

  1. 创建一个空 WPF 程序,按照 WPF dotnet core 如何开启 Pointer 消息的支持 博客提供的方法开启 WM_Pointer 消息
  2. 在 MainWindow 放入 InkCanvas 控件
  3. 准备好环境,最简环境是有两个屏幕,其中副屏是触摸屏。运行程序,将程序的主窗口移动到副屏上,对应用程序进行触摸

此时你将会发现应用程序无法绘制出你所画出的笔迹。当你将程序移动到主屏幕上时,如果恰好此时你的主屏幕也是触摸屏,那你将可以看到应用程序实际是能正常工作的,画出你触摸的笔迹。如果你将整个窗口缩放很大,跨了你的两个屏幕,你将会发现在副屏上所画的内容将会显示到主屏幕上去。且所偏差的坐标差值刚好是整个屏幕距离,如果刚好你的两个屏幕的虚拟尺寸(非物理尺寸)是一样大的,那这个偏差就更好看出来了,如下图

问题原因:

此问题是在 WPF 框架中的 HwndPointerInputProvider.cs 的代码实现不正确导致的,和 WM_Pointer 本身关系不大,仅仅只是因为这个代码实现只有开启了 WM_Pointer 才会进入。在 HwndPointerInputProvider 的 GetOriginOffsetsLogical 方法里面,没有考虑多屏幕的坐标系问题,只是计算了当前窗口所在的屏幕的坐标,没有考虑应该计算整个大的虚拟屏幕的坐标

 private void GetOriginOffsetsLogical(out int originOffsetX, out int originOffsetY) 
 { 
     Point originScreenCoord = _source.Value.RootVisual.PointToScreen(new Point(0, 0)); 
  
     // Use the inverse of our logical tablet to screen matrix to generate tablet coords 
     MatrixTransform screenToTablet = new MatrixTransform(_currentTabletDevice.TabletToScreen); 
     screenToTablet = (MatrixTransform)screenToTablet.Inverse; 
  
     Point originTabletCoord = originScreenCoord * screenToTablet.Matrix; 
  
     originOffsetX = (int)Math.Round(originTabletCoord.X); 
     originOffsetY = (int)Math.Round(originTabletCoord.Y); 
 }

以上代码的错误点在于 Point originScreenCoord = _source.Value.RootVisual.PointToScreen(new Point(0, 0)) 计算的是当前窗口所在的显示器的窗口左上角的相对坐标。正确的实现应该考虑当前窗口所在屏幕的虚拟屏幕坐标,如以下 少珺 小伙伴修复后的代码

        private void GetOriginOffsetsLogical(out int originOffsetX, out int originOffsetY)
        {
            Point originScreenCoord = new Point();

            HwndSource hwndSource = PresentationSource.FromVisual(_source.Value.RootVisual) as HwndSource;
            if (hwndSource != null)
            {
                HandleRef handleRef = new HandleRef(hwndSource, hwndSource.CriticalHandle);

                MS.Win32.NativeMethods.POINT point = new MS.Win32.NativeMethods.POINT();
                MS.Win32.UnsafeNativeMethods.ClientToScreen(handleRef, ref point);

                var displayRect = _currentTabletDevice.DeviceInfo.DisplayRect;

                originScreenCoord = new Point(point.x - displayRect.left, point.y - displayRect.top);
            }

            // Use the inverse of our logical tablet to screen matrix to generate tablet coords
            MatrixTransform screenToTablet = new MatrixTransform(_currentTabletDevice.TabletToScreen);
            screenToTablet = (MatrixTransform)screenToTablet.Inverse;
            Point originTabletCoord = originScreenCoord * screenToTablet.Matrix;
            originOffsetX = (int)Math.Round(originTabletCoord.X);
            originOffsetY = (int)Math.Round(originTabletCoord.Y);
        }

以上代码先算出当前窗口的左上角相对于屏幕的坐标,也就是 MS.Win32.UnsafeNativeMethods.ClientToScreen(handleRef, ref point); 所实现的内容。再经过 _currentTabletDevice.DeviceInfo.DisplayRect 属性获取当前窗口所在屏幕的虚拟屏幕坐标,将上一步计算到的窗口相对于屏幕的坐标减去当前的屏幕的虚拟坐标才是计算到正确的坐标值

详细更改请参阅 https://github.com/dotnet-campus/wpf/pull/9

我将 少珺 小伙伴修复后的代码合入到 https://github.com/dotnet-campus/dotnetCampus.CustomWpf/ 仓库里面,这个 CustomWpf 仓库是我所在的团队用来对 WPF 框架进行日常的开发测试所使用的仓库,相对 WPF 仓库来说会更加激进一点。合入之后,我打出了 NuGet 包,大家可以通过编辑 csproj 项目文件,添加以下代码使用到此测试版本的 WPF 框架

  <ItemGroup>
    <PackageReference Include="dotnetCampus.WPF.Resource" Version="6.0.4-alpha07-test06" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="$(CustomWpfAssetsFolder)lib\net6.0\*.dll" />
    <ReferenceCopyLocalPaths Include="$(CustomWpfAssetsFolder)lib\net6.0\*.dll" />
  </ItemGroup>

更多关于 WM_Pointer 的坑,请看 WPF 开启Pointer消息存在的坑

更多关于 WPF 的博客请看 博客导航

标签:Point,触摸,WM,坐标,WPF,Pointer,屏幕
From: https://www.cnblogs.com/lindexi/p/17889592.html

相关文章

  • WPF 已知问题 包含 NaN 的 Geometry 几何可能导致渲染层抛出 UCEERR_RENDERTHREADFAIL
    本文记录一个WPF已知问题,当传入到渲染的Geometry几何里面包含了NaN数值,将可能让应用程序收到从渲染层抛上来的UCEERR_RENDERTHREADFAILURE异常,且此异常缺乏必要信息,比较难定位到具体错误逻辑此问题是小伙伴报告给我的,详细请看https://github.com/dotnet/wpf/issues/7421......
  • WPF 尝试使用 WinML 做一个简单的手写数字识别应用
    最近我看了微软的AI训练营之后,似乎有点了解WindowsMachineLearning和DirectML的概念,于是我尝试实践一下,用WPF写一个简单的触摸手写输入的画板,再使用大佬训练好的mnist.onnx模型,对接WinML实现一个简单的手写数字识别应用本文属于WinML的入门级博客,我将尝试一步步......
  • WPF 的 WriteableBitmap 在 Intel 11 代 Iris Xe Graphics 核显设备上停止渲染
    在Intel11代锐炬Intel®Iris®XeGraphics核显设备上,如果此设备使用旧版本驱动,则可能导致WPF的WriteableBitmap停止渲染。此问题和WPF无关,此问题是Intel的bug且最新驱动版本已修复官方问题记录地址:https://www.intel.cn/content/www/cn/zh/support/articles/000......
  • WPF 的 Viewport3D 等 3D 模块在带 Intel UHD 770 设备上抛出渲染异常
    在带IntelUHD770的设备上,使用旧版本驱动,即小于30.0.101.1660版本驱动,将会导致WPF的3D模块出现渲染异常。此问题和WPF无关,此问题是Intel的bug且最新驱动版本已修复官方问题记录地址:https://community.intel.com/t5/Graphics/Crash-with-UHD-770-in-WPF-applicatio......
  • 【转】[C#][WPF] 避免窗口最大化时遮盖任务栏
    转自:https://learn.microsoft.com/zh-cn/previous-versions/msdn10/dd366102(v=MSDN.10)WPF窗口最大化时有个很不好的现象是:如果窗口的WindowStyle被直接或间接地设置为None后(比如很多情况下你会覆盖默认的窗体样式,即不采用Windows默认的边框和最大化最等按钮,来打造个性的窗......
  • EdrawMax v13 解锁版下载及安装教程 (综合图形图表设计软件)
    前言万兴亿图图示(WondershareEdrawMax)是一款综合图形图表设计软件,Visio国产替代.亿图图示中文版(EdrawMax)是一款办公绘图软件的思维导图软件.无需任何绘图功底,即可轻松创建各类思维导图.亿图图示专家,提供大量事例和在线模板,用于创建流程图,信息图,组织结构图,科学教育插......
  • 使用WM_COPYDATA实现进程间通信
    发送端LRESULTcopyData;//copyDataResulthasvaluereturnedbyotherappCWnd*pOtherWnd=CWnd::FindWindow(NULL,_T("窗体名"));CStringstrData;strData.Format(L"%.1lf",tickdata);if(pOtherWnd){ COPYDATASTRUCTcpd;//上面提到的结构体 cpd.......
  • WPF UNO 测试固定尺寸且水平和垂直对齐设置 Stretch 的元素在容器内的布局行为
    本文将告诉大家我对WPF的自定义布局容器和自定义控件进行的布局行为测试中的一个小点,即测试固定元素的尺寸的情况下或元素尺寸为有限尺寸的情况下,同步设置元素的水平和垂直对齐为Stretch来测试元素在容器内的布局行为,元素分别在容器给元素的布局尺寸大于元素的尺寸和小于元素......
  • 理解Window和WIndowManager
    Window表示一个窗口的概念,在日常开发中直接接触Window的机会并不多,但是在某些特殊时候我们需要在桌面上显示一个类似悬浮窗的东西,那么这种效果就需要用到Window来实现。Window是一个抽象类,它的具体实现是PhoneWindow。创建一个Window是很简单的事,只需要通过WindowManager即......
  • Jenkins 编译 .NET 6 WPF
    最近公司需求要将产品编译自动化,干了那么多年客户端开发一直都是小作坊作业最近换了一个比较正规的互联网公司一切都需要标准化流程化了,自动化也必不可少!然后我就了解到了Jenkins这玩意,找了两天资料感觉还挺简单的写篇文章收录下。因为签名UKey只要windows驱动,所以我只能将环境......