天天看点

VB里怎样使用GetWindowRect、GetClientRect、ScreenToClient、ClientToScreen一.常规方法能获得的准确值二.API方法能做到的什么三.常见API应用举例四.关于API函数MoveWindow()

网上查了半天都没有讲清楚,为什么都喜欢把Form的坐标搞成(0,0),特例是不能当通用方法来用的,随意摆放的位置才有比对学习的价值。系统地学习、测试了一下,弄明白了。

百度百科里说的窗口,实际上适用于Form/控件
VB里怎样使用GetWindowRect、GetClientRect、ScreenToClient、ClientToScreen一.常规方法能获得的准确值二.API方法能做到的什么三.常见API应用举例四.关于API函数MoveWindow()

目录

一.常规方法能获得的准确值

二.API方法能做到的什么

1.针对Form

2.针对控件

三.常见API应用举例

1.怎样获取任一控件的尺寸,及在其容器对象中的坐标

(1)入门思路

(2)归纳为一条标准化的套路

(3)再精进一步

2.待续...

四.关于API函数MoveWindow()

一.常规方法能获得的准确值

【Form】在屏幕中的位置及尺寸:(100,100,839,390)

【Form】的客户区坐标系:(0,0)-(823,351)

【控件】在客户区坐标系中的位置及尺寸:(80,40,97,46)

二.API方法能做到的什么

1.针对Form

【Form】在屏幕坐标系中的位置及尺寸:(100,100,939,490)

【Form】的客户区坐标系:(0,0,823,351)

将屏幕坐标系中的点p(0,0)转换为【Form】的客户区坐标:(-108,-131)

将【Form】的客户区坐标系中的点p(0,0)转换为屏幕坐标:(108,131)

2.针对控件

【控件】在屏幕坐标系中的位置及尺寸:(188,171,285,217)

【控件】的客户区坐标系:(0,0,97,46)

将屏幕坐标系中的点p(0,0)转换为【控件】客户区坐标:(-188,-171)

将【控件】的客户区坐标系中的点p(0,0)转换为屏幕坐标:(188,171)

相应代码如下:

Option Explicit

'获取指定窗口(Form/控件)的位置及尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出。
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

'获取窗口(Form/控件)客户区的坐标系。客户区坐标指定客户区的左上角和右下角,即(0,0)-(ScaleWidth,ScaleHeight)。
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

'将屏幕坐标系中的点转换为客户区坐标
Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long

'将客户区坐标系中的点转换为屏幕坐标
Private Declare Function ClientToScreen Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long

'给RECT变量赋值
Private Declare Function SetRect Lib "user32" (lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) 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

Private Sub Form_Load()
    Dim rec As RECT
    Dim recWin1 As RECT, recCli1 As RECT, recWin2 As RECT, recCli2 As RECT, p As POINTAPI
    
    Me.Move 100 * Screen.TwipsPerPixelX, 100 * Screen.TwipsPerPixelY
    Command1.Move 80, 40
    
    '常规方法能获得的准确值
    addLog3 "常规方法能获得的准确值", ""
    SetRect rec, toX(Me.Left), toY(Me.Top), toX(Me.Width), toY(Me.Height)
    addLog "【Form】在屏幕中的位置及尺寸", rec
    addLog3 "【Form】的客户区坐标系", "(0,0)-(" & Me.ScaleWidth & "," & Me.ScaleHeight & ")"
    SetRect rec, Command1.Left, Command1.Top, Command1.Width, Command1.Height
    addLog "【控件】在客户区坐标系中的位置及尺寸", rec, True
    
    '下面是API方法能做到的
    addLog3 "下面是API方法能做到的", ""
    '1.针对Form
    addLog3 "一.针对Form", ""
    '获取【Form】在屏幕坐标系中的位置及尺寸
    GetWindowRect Me.hwnd, recWin1
    addLog "【Form】在屏幕坐标系中的位置及尺寸", recWin1
    
    '获取【Form】的客户区坐标系
    GetClientRect Me.hwnd, recCli1
    addLog "【Form】的客户区坐标系", recCli1
    
    '将屏幕坐标系中的点p(0,0)转换为客户区坐标
    p.X = 0: p.Y = 0
    ScreenToClient Me.hwnd, p
    addLog2 "将屏幕坐标系中的点p(0,0)转换为【Form】的客户区坐标", p
    
    '将客户区坐标系中的点p(0,0)转换为屏幕坐标
    p.X = 0: p.Y = 0
    ClientToScreen Me.hwnd, p
    addLog2 "将【Form】的客户区坐标系中的点p(0,0)转换为屏幕坐标", p, True
    
    '2.针对控件
    addLog3 "二.针对控件", ""
    '获取【控件】在屏幕坐标系中的位置及尺寸
    GetWindowRect Command1.hwnd, recWin1
    addLog "【控件】在屏幕坐标系中的位置及尺寸", recWin1
    
    '获取【控件】的客户区坐标系
    GetClientRect Command1.hwnd, recCli1
    addLog "【控件】的客户区坐标系", recCli1
    
    '将屏幕坐标系中的点p(0,0)转换为【控件】的客户区坐标
    p.X = 0: p.Y = 0
    ScreenToClient Command1.hwnd, p
    addLog2 "将屏幕坐标系中的点p(0,0)转换为【控件】客户区坐标", p
    
    '将【控件】客户区坐标系中的点p(0,0)转换为屏幕坐标
    p.X = 0: p.Y = 0
    ClientToScreen Command1.hwnd, p
    addLog2 "将【控件】的客户区坐标系中的点p(0,0)转换为屏幕坐标", p, True
End Sub

Private Function toX(X As Long)
    toX = X / Screen.TwipsPerPixelX
End Function
Private Function toY(Y As Long)
    toY = Y / Screen.TwipsPerPixelY
End Function
Private Sub addLog(sTitle, rec As RECT, Optional dblcrlf As Boolean)
    With rec
        Text1.Text = Text1.Text & sTitle & ":(" & .Left & "," & .Top & "," & .Right & "," & .Bottom & ")" & vbCrLf & IIf(dblcrlf, vbCrLf, "")
    End With
End Sub
Private Sub addLog2(sTitle, p As POINTAPI, Optional dblcrlf As Boolean)
    With p
        Text1.Text = Text1.Text & sTitle & ":(" & .X & "," & .Y & ")" & vbCrLf & IIf(dblcrlf, vbCrLf, "")
    End With
End Sub
Private Sub addLog3(sTitle, log As String, Optional dblcrlf As Boolean)
    Text1.Text = Text1.Text & sTitle & ":" & log & vbCrLf & IIf(dblcrlf, vbCrLf, "")
End Sub
           

三.常见API应用举例

1.怎样获取任一控件的尺寸,及在其容器对象中的坐标

题意:已知一个控件的句柄,求其尺寸及在其容器对象中的坐标

代表性:可求任意控件的相对坐标及其尺寸,只要有句柄就可以

应用场景:在界面控制中,有时需要将某个控件从容器A搬到容器B中显示,最后还能搬回到容器A中,这时就需要在控件搬离容器A前记住控件在容器A中的位置。如果该控件是自定义控件,事先是不知道它被置于什么容器里的,这种情况下求控件在其容器中的坐标,常规方法是有困难的,如容器客户区坐标系的单位不能指望是统一的,什么时候需要转换,怎样转换都是难题,再比如该容器可能没暴露句柄属性,使用API就受到限制。

以上面的示例为例,Command1的容器对象是Form1,这里用API来求一下Command1在Form1中的坐标及尺寸

VB里怎样使用GetWindowRect、GetClientRect、ScreenToClient、ClientToScreen一.常规方法能获得的准确值二.API方法能做到的什么三.常见API应用举例四.关于API函数MoveWindow()

(1)入门思路

第一步:求Command1的屏幕坐标

VB里怎样使用GetWindowRect、GetClientRect、ScreenToClient、ClientToScreen一.常规方法能获得的准确值二.API方法能做到的什么三.常见API应用举例四.关于API函数MoveWindow()
Dim recWin As RECT
GetWindowRect Command1.hwnd, recWin
           

变量recWin即为矩形CDEF在屏幕坐标系A中位置,C点坐标为(recWin.Left,recWin.Top),E点坐标为(recWin.Right,recWin.Bottom),

所以,Command1的尺寸为:

宽度=recWin.Right-recWin.Left

高度=recWin.Bottom-recWin.Top

但recWin描述的是Command1的屏幕坐标,我们需要的是Command1在Form1客户区中的坐标,明显,需要用ScreenToClient()做坐标转换。下面将C点的屏幕坐标转换为Form1客户区中的坐标。

Dim C As POINTAPI
C.X = recWin.Left
C.Y = recWin.Top
ScreenToClient Form1.hwnd, C
           

这样我们就得到了C点在From1客户区中的坐标,也就是Command1在Form1客户区中的坐标,即

Command1的左上角坐标=(C.X,C.Y)

(2)归纳为一条标准化的套路

上面的求法有点晦涩,不如直接将C点和E点的屏幕坐标转换都转换成在Form1客户区中的坐标,我们用一个RECT变量来存储,赋予其含义为控件在Form1客户区中的矩形坐标,更便于理解、存储、作为参数传递,形成形式化的套路,就记得牢靠了。代码如下:

'功能:获取一个控件在某容器客户区中矩形坐标
'参数:ctlHwnd,控件句柄;clientHwnd:容器句柄
Private Function GetCtlRect(ctlHwnd As Long, clientHwnd As Long) As RECT
    '获取控件的屏幕坐标
    Dim recWin As RECT
    GetWindowRect ctlHwnd, recWin
    
    '将C点的屏幕坐标转换为客户区坐标
    Dim C As POINTAPI
    C.X = recWin.Left
    C.Y = recWin.Top
    ScreenToClient clientHwnd, C
    
    '将E点的屏幕坐标转换为客户区坐标
    Dim E As POINTAPI
    E.X = recWin.Right
    E.Y = recWin.Bottom
    ScreenToClient clientHwnd, E
    
    '将控件矩形的客户区坐标收容到RECT存储起来
    Dim recForm As RECT
    SetRect recForm, C.X, C.Y, E.X, E.Y
    
    GetCtlRect = recForm
End Function

Private Sub Command2_Click()
    Dim rec As RECT
    rec = GetCtlRect(Command1.hwnd, Form1.hwnd)
    
    addLog "Command1在Form1客户区中的坐标", rec, True
End Sub
           

(3)再精进一步

有些容器对象没有暴露其句柄属性的,又当怎样求控件在父容器客户区中的矩形坐标呢?实际上,我们要解决的是怎样得到容器的句柄,可用另一个API函数——GetParent(),上述代码演变成如下:

'获取父容器的句柄
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long

Private Sub Command2_Click()
    Dim rec As RECT
    rec = GetCtlRect(Command1.hwnd, GetParent(Command1.hwnd))
    
    addLog "Command1在Form1客户区中的坐标", rec, True
End Sub
           

2.待续...

四.关于API函数MoveWindow()

'挪动控件/窗口

Private Declare Function MoveWindow Lib "user32" (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long

要强调的是,这个挪动是指在被挪动对象的父容器中挪动,即调用API的坐标参数是参照父容器客户区坐标系的描述。这是特定的,不能想入非非,哪儿哪儿瞎用。

继续阅读