Forms 访问表单子类化以捕获WM_移动消息

Forms 访问表单子类化以捕获WM_移动消息,forms,ms-access,vba,64-bit,Forms,Ms Access,Vba,64 Bit,我是这个论坛的新手,所以提前感谢你对这个帖子的帮助 我正在试图解决我认为是一个古老的访问表单问题。我正在开发一个数据库,在这个数据库中,一个弹出式主窗体将打开我希望保留在主窗体边界内的其他几个弹出式窗体。当主窗体被移动或调整大小时,我需要相应地移动和调整其他打开窗体的大小。我基本上是想让我的数据库看起来像一个真正的应用程序,其中一个主窗体有几个子窗体 我正在使用event Detail_Paint()来检测主窗体的大小何时更改,以调整其他窗体的大小,并且它似乎可以工作。然而,在窗体的屏幕上并没有

我是这个论坛的新手,所以提前感谢你对这个帖子的帮助

我正在试图解决我认为是一个古老的访问表单问题。我正在开发一个数据库,在这个数据库中,一个弹出式主窗体将打开我希望保留在主窗体边界内的其他几个弹出式窗体。当主窗体被移动或调整大小时,我需要相应地移动和调整其他打开窗体的大小。我基本上是想让我的数据库看起来像一个真正的应用程序,其中一个主窗体有几个子窗体

我正在使用event Detail_Paint()来检测主窗体的大小何时更改,以调整其他窗体的大小,并且它似乎可以工作。然而,在窗体的屏幕上并没有和移动相关的事件。MouseMove()似乎不起作用,因为当用户在表单标题上移动鼠标光标时,它不会触发。我用一个计时器解决了这个问题,它每10毫秒检查一次主窗体的位置,并相应地改变其他窗体的位置。然而,当用户在控件中输入文本时,这会产生令人讨厌的监视器闪烁以及问题

我已经读到,可以对表单进行子类化,并捕获WM_移动消息到窗口。我已经为此开发了一些测试代码,但当我尝试运行它时,Access停止工作,我必须使用任务管理器将其关闭。我在Windows 10 64位系统上使用Access 2016 Professional 64位

这是我到目前为止编写的代码

' This code goes into a general module (mdl_subclass)
' When subclassing shows the coordinates of the window in its caption

Option Explicit

Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias     "SetWindowLongPtrA" _
        (ByVal hWnd As LongPtr, _
         ByVal nIndex As LongPtr, _
         ByVal dwNewLong As LongPtr) As LongPtr

Declare PtrSafe Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _
        (ByVal lpPrevWndFunc As LongPtr, _
         ByVal hWnd As LongPtr, _
         ByVal Msg As LongPtr, _
         ByVal wParam As LongPtr, _
         ByVal lParam As LongPtr) As LongPtr

Public Const GWL_WNDPROC = (-4)
Private Const WM_MOVE = &H3

Dim m_PrevProc As LongPtr

Public Sub SubClass_On(ByVal hWnd As Long)
  m_PrevProc = SetWindowLongPtr(hWnd, GWL_WNDPROC, AddressOf WindowProc)
End Sub

Public Sub SubClass_Off(ByVal hWnd As Long)
  SetWindowLongPtr hWnd, GWL_WNDPROC, m_PrevProc
End Sub

Private Function WindowProc(ByVal hWnd As LongPtr, ByVal uMsg As LongPtr,      ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr

  WindowProc = CallWindowProc(m_PrevProc, hWnd, uMsg, wParam, lParam)

  If uMsg = WM_MOVE Then

    Form_frm_main.Me_OnMove lParam And CLng(&HFFFF&), lParam \   CLng(&HFFFF&)

  End If

End Function


' This code instead goes into the form module, starts subclassing on form   loading and stop subclassing on form unload

Private Sub Form_Load()

  SubClass_On Me.hWnd

End Sub

Private Sub Form_Unload(Cancel As Integer)

  SubClass_Off Me.hWnd

End Sub


Friend Sub Me_OnMove(ByVal xPos As Long, ByVal yPos As Long)

  Me.Caption = "x=" & xPos & "; y=" & yPos

End Sub
…你知道如何修复我的代码吗?或者对我的问题有其他的建议


谢谢

我想我已经找到了解决问题的办法! 我尝试重新创建的是一个带有Access窗体的MDI应用程序环境。我发现了一个API函数,它允许将窗体的属性设置为子级。函数为SetParent()

如果尝试在任何access窗体的Form_Open()事件中使用此函数,它会将该窗体视为任何其他指定窗体的子窗体。设置子属性后,Windows将自动将子窗体与主窗体一起移动到屏幕上的任何位置。效果非常好

这里有一个例子

在模块中声明以下WinAPI函数

Public Declare PtrSafe Function SetParent Lib "user32" _
                               (ByVal hWndChild As LongPtr, _
                                ByVal hWndParent As LongPtr) As Long

Public Declare PtrSafe Function SetWindowPos Lib "user32" _
                               (ByVal hwnd As LongPtr, _
                                ByVal hWndInsertAfter As LongPtr, _
                                ByVal x As Long, _
                                ByVal y As Long, _
                                ByVal cx As Long, _
                                ByVal cy As Long, _
                                ByVal wFlags As Long) As Long
注意:我使用LongPtr是因为我在64位环境中工作。 然后,在子窗体_open事件中,使用以下代码

Private Sub Form_open(Cancel As Integer)

  Dim hWndParent As LongPtr
  Dim hWndChild As LongPtr

  hWndParent = Form_frm_parent.hwnd
  hWndChild = Form_frm_child.hwnd

  SetParent hWndChild, hWndParent   ' open the form as a child

                                    ' this is used to position the form
  SetWindowPos hWndChild, hWndParent, 163, 44, 725, 437, &H4

End sub
我只在弹出式窗体上试用过,所以我不知道它是否也适用于其他窗体。但是,如果将两个窗体(父窗体和子窗体)都设置为弹出窗体,则Windows会自动将子窗体与父窗体一起移动。您还可以添加子窗体的子窗体,它仍然可以很好地工作

但是,一旦将表单声明为子表单,命令docmd.move就会产生一些奇怪的效果。更好的方法是使用WinAPI函数SetWindowPos定位子窗体。坐标系将相对于父窗体的位置。因此,0,0坐标是父窗体的左上角

我发现使用函数GetWindowRect获取父窗体的坐标,然后设置子窗体的位置非常有用

Public Type RECT
    wdw_left As Long
    wdw_top As Long
    wdw_right As Long
    wdw_bottom As Long
End Type

Public Declare PtrSafe Function GetWindowRect Lib "user32" _
                               (ByVal hwnd As LongPtr, _
                                lpRect As RECT) As Long
然后在子窗体的form_resize()事件中

Dim hWndParent As LongPtr   ' handle finestra
Dim hWndChild As LongPtr    ' handle finestra
Dim mainRECT As RECT        ' coordinate form_home (struct RECT)

hWndParent = Form_frm_parent.hwnd
hWndChild = Form_frm_child.hwnd

GetWindowRect hWndParent, mainRECT
一旦你有了主窗体的坐标,你就可以决定你的子窗体的位置。最后,如果要根据父窗体的大小动态调整子窗体的位置,可以使用父窗体的form_resize()事件调用子窗体的form_resize()事件并更改其大小和位置。为了做到这一点,子表单的_resize()必须声明为public,然后您可以从任何其他表单(本例中的父表单)调用它。这样,如果父窗体太小,可以向子窗体添加滚动条

我希望这将帮助对这种解决方案感兴趣的任何其他用户


干杯

你的声明是错误的,nIndex和Msg很长。很有趣。你能给你的答案添加一个例子吗,你是如何使用API函数的?嗨@Andre,我已经编辑了我的答案,并添加了一些代码和解释……我希望它能有所帮助!:)非常感谢,我一直在处理这个问题,明天我会试试,希望它能奏效!Access窗体没有适当的FormMove事件,因此您可以轻松手动重新定位某些对象。我忘了这一点,它似乎似曾相识,意义非凡。