本文介绍了赢得10 Excel 2016无法解释的PixelsToPoints系数来定位UserForm的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Preambule

当尝试将UserForm定位在特定的Pixel位置(存储在 POINTAPI Type结构中)时,必须将Pixel坐标转换为Point坐标才能设置 UserForm.Left UserForm.Top VBA属性.我们称这个系数为K.

When trying to position a UserForm at a particular Pixel position (stored in a POINTAPI Type structure), one must convert the Pixel coordinates to Point coordinates to be able to set the UserForm.Left and UserForm.Top VBA properties. Let's call this coefficient K.

从测试中,我了解到我的情况是 GetWindowRect 和UserForm的VBA定位属性( Top Width Height )包括包含MSForm UserForm控件的窗口("ThunderDFrame"类)周围的阴影.为了真正获得由边框分隔的窗口矩形,必须使用 DwnGetWindowAttribute(hWnd,DWMWA_EXTENDED_FRAME_BOUNDS,rcOutRECT,LenB(rcOutRECT) Win API.

From my tests, I came to understand that in my case, GetWindowRect and the VBA positioning properties of a UserForm (Left, Top, Width, Height) includes the shadows around the window (of class "ThunderDFrame") containing the MSForm UserForm control. To really get the rectangle of the window delimited by the borders, DwnGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, rcOutRECT, LenB(rcOutRECT) Win API must be used.

放置用户窗体的坐标系的原点是像素(0; 0),因此无需担心 ActiveWindow.PointsToScreenPixelsX / ActiveWindow.PointsToScreenPixelsY 和Excel窗口的左上角和工作表网格的左上角之间的偏移量(至少直到 Range.Left Range.Top 等属性出现)发挥作用).但是,有趣的是, ActiveWindow.PointsToScreenPixelsX 的行为不像 ActiveWindow.ActivePane.PointsToScreenPixelsX .第一个与像素一起使用的是输入,而不是点,第二个则与像素相同.该方法的真实名称应该是 ActiveWindow.WorksheetPixelsXToScreenPixelsX .您可以轻松地对其进行验证:

The origin of the coordinate system to position a UserForm is pixel (0; 0), so no need to worry about ActiveWindow.PointsToScreenPixelsX / ActiveWindow.PointsToScreenPixelsY and the offset between the top left corner of the Excel window and the top left corner of the worksheet grid (at least not until Range.Left, Range.Top, etc. properties come into play). However, it is interesting to note that ActiveWindow.PointsToScreenPixelsX does not behave like ActiveWindow.ActivePane.PointsToScreenPixelsX. The first one works with pixels has input, not points, as the second does. The real name of the method should rather be ActiveWindow.WorksheetPixelsXToScreenPixelsX. You can verify it easily:

ActiveWindow.PointsToScreenPixelsX(1) - ActiveWindow.PointsToScreenPixelsX(0)

返回1,而如果它确实在进行转换,则应该返回大于1的值,因为1点占据了屏幕上的几个像素.(由于像素的整数舍入,因此也不是1/K)

returns 1, while if it was really doing a convertion it should return something greater than 1 since 1 Point occupy several Pixels on screen. (not really 1/K either because of integer rounding of pixels)

问题

为了简化我的MCV示例,将缩放系数考虑为1,用系数来确定用户窗体的点(x)中的 .Left .Top 属性.y)我们希望在屏幕像素中显示的位置应该是:

Considering the zoom factor is 1 to simplify my MCV Example, the coefficients to determine the .Left and .Top properties in Points of the UserForm from the (x; y) position in Screen Pixels at which we want it displayed should be :

72 / GetDeviceCaps(GetDC(0), LOGPIXELSX)
72 / GetDeviceCaps(GetDC(0), LOGPIXELSY)

  • 0.75用于96 DPI旧显示(我已经在使用Win 7 + Excel 2007的PC上尝试过
  • 0.375的Surface Pro 4平板电脑在带有Excel 2016 32位的Win 10 64位上运行

现在的问题是,在我的平板电脑上,虽然上面的计算返回0.375,但将UserForm定位在给定像素位置的正确系数(从 GetCursorPos 获得)例如将API转换为相应的Point位置为0.35 .我不知道该价值从何而来... ???

Now the problem is that on my tablet, while the above calculation returns 0.375, the correct coefficient to position a UserForm at a given Pixel position (obtained from GetCursorPos Win API for example) by converting it to the corresponding Point position is 0.35. I have no idea where that value comes from...???

当前进度

在平板电脑上:

reg键 HKEY_CURRENT_USER \ Control Panel \ Desktop \ WindowMetrics \ AppliedDPI 表示192,并且 72/192 = 0.375

reg key HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics\AppliedDPI indicates 192 and 72 / 192 = 0.375

我还尝试了MSDN的Windows Destop App UI参考中的High DPI参考中的某些功能:

I have also tried some function from the High DPI Reference in MSDN's Windows Destop App UI Reference:

  • GetDPIForWindow (我尝试使用Application.Hwnd和UserForm的窗口句柄)
  • GetDPIForMonitor
  • GetDPIForWindow (I tried with Application.Hwnd and UserForm's window handle)
  • GetDPIForMonitor

但是一切正常返回192.

but everything rightfully returns 192.

最小,完整和可验证的示例

以下内容使我可以在平板电脑上检索神秘的K = 0.35系数,但按预期在另一台计算机上返回0.75.

The following allows me to retrieve the mysterious K = 0.35 coefficient on my tablet, but returns 0.75 on the other computer, as expected.

Private Declare Function GetWindowRect Lib "user32" (ByVal hWnd As Long, rcWindowRect As RECT) As Long
Private Declare Function GetCursorPos Lib "user32" (ptCursorPoint As POINTAPI) As Long

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type POINTAPI
    X As Long
    Y As Long
End Type

Sub test()
    Dim rcUsfWindowRect As RECT
    UserForm1.Show vbModeless
    lRet& = GetWindowRect(UserForm1.hWnd, rcUsfWindowRect)
    dblUsfRectWidth# = rcUsfWindowRect.Right - rcUsfWindowRect.Left
    dblUsfRectHeight# = rcUsfWindowRect.Bottom - rcUsfWindowRect.Top
    Debug.Print UserForm1.Width / dblUsfRectWidth
End Sub
Private Declare Function FindWindowA Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public hWnd As Long

Private Sub UserForm_Initialize()
    hWnd = FindWindowA("ThunderDFrame", UserForm1.Caption)
End Sub

推荐答案

我遇到了同样的问题.我在64位上尝试了您的代码,还收到了0.35.

I have the same issues. I tried your code on 64 bit and also received 0.35.

Option Explicit

Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hWnd As Long, rcWindowRect As RECT) As Long
Private Declare PtrSafe Function GetCursorPos Lib "user32" (ptCursorPoint As POINTAPI) As Long

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type POINTAPI
    X As Long
    Y As Long
End Type

Sub test()
    Dim rcUsfWindowRect As RECT
    Dim dblUsfRectWidth As Double
    Dim dblUsfRectHeight As Double

    UserForm1.Show vbModeless
    Call GetWindowRect(UserForm1.hWnd, rcUsfWindowRect)
    dblUsfRectWidth = rcUsfWindowRect.Right - rcUsfWindowRect.Left
    dblUsfRectHeight = rcUsfWindowRect.Bottom - rcUsfWindowRect.Top
    Debug.Print UserForm1.Width / dblUsfRectWidth
End Sub
Option Explicit

Private Declare PtrSafe Function FindWindowA Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public hWnd As Long

Private Sub UserForm_Initialize()
    hWnd = FindWindowA("ThunderDFrame", UserForm1.Caption)
End Sub

奇怪的是,我下载了一个标尺应用程序,以便能够以像素为单位测量UserForm1的宽度.我在Excel中将窗体的宽度调整为500,结果得到的以像素为单位的测量宽度为1406.并且500/1406 = 0.356,而不是0.375.我还找出了为什么它是0.356而不是0.35的原因,这是因为GetWindowRect函数显然返回了一个宽度,该宽度包括边框周围的阴影.(左侧为15或16像素,右侧为10像素).没有这个,您将拥有1406 + 15 + 10 = 1431和500/1431 = 0.349的宽度,该宽度更接近于0.35.

The strange this is that I downloaded a ruler app to be able to measure the width of the UserForm1 in pixels. I adjusted the Width of the Form in Excel to 500 and as a result I get a measured width in pixels of 1406. And 500/1406 = 0.356 and not 0.375.I also found out why it's 0.356 and not 0.35, this is because the GetWindowRect function returns apparently a width including shadows around the Border. (left this is 15 or 16 px and right 10 px). Without this you would have a width of 1406 + 15 + 10 = 1431 and 500/1431 = 0.349, which is closer to the 0.35.

我以前的笔记本电脑没有这些问题,也没有外接显示器.这仅发生在高DPI宽度的监视器上.(可能是因为在Windows的这些监视器上启用了显示虚拟化).

I didn't have these problems on my previous laptop and I don't have them with an external monitor. This happens only width high DPI monitors. (probably because display virtualization is enabled on these monitors in windows).

如果我将Windows的缩放比例更改为100%,然后注销并重新登录,则得到的值为0.75,一切都会按预期进行.

If I change the zoom in windows to 100% log out and log back in I get a value of 0.75 and everything works as expected.

这篇关于赢得10 Excel 2016无法解释的PixelsToPoints系数来定位UserForm的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 07:02