访问表单子类化以捕获 WM_MOVING 消息
Access Form subclassing to catch WM_MOVING message
我是这个论坛的新手,所以在此先感谢您对这个话题的帮助。
我正在尝试解决我认为是 Access Forms 的 age-old 问题。我正在开发一个数据库,其中 pop-up 主窗体打开其他几个 pop-up 窗体,我希望它们保留在主窗体的边界内。当主窗体移动或调整大小时,我需要相应地移动和调整其他打开的窗体的大小。我基本上是想让我的数据库看起来像一个主窗体有多个 child 窗体的真实应用程序。
我正在使用事件 Detail_Paint() 来检测何时更改主窗体的大小以调整其他窗体的大小,它似乎有效。但是,没有与表单屏幕上的移动相关联的事件。 MouseMove() 似乎不起作用,因为当用户在表单标题上移动鼠标光标时,它不会触发。我已经使用计时器解决了这个问题,该计时器每 10 毫秒检查一次主窗体的位置并相应地更改其他窗体的位置。但是,这会造成令人讨厌的显示器闪烁,并且还会在用户在控件中输入文本时出现问题。
我读到可以将表单子类化并将 WM_MOVING 消息捕获到 window。我为此开发了一些测试代码,但是当我尝试 运行 它时,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 Forms 的 MDI 应用程序环境。我找到了一个 API 函数,它允许将表单的 属性 设置为 child。函数是SetParent().
如果您尝试在任何访问表单的 Form_Open() 事件中使用此函数,它将将该表单视为任何其他指定表单的 child。一旦设置了 child 属性,Windows 将自动将 child 表单与主表单一起移动到屏幕上的任何位置。效果很好!
这是一个例子。
在模块中声明以下 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 位环境中工作。
然后,在childform_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
我只尝试过 pop-up 表格,所以我不知道它是否也适用于其他表格。但是,如果您将两种形式(parent 和 child)设置为 pop-up 形式,您将看到 Windows 将 child 形式与 parent 自动形成。您还可以添加 child 的 child 形式,它仍然可以很好地工作。
但是,一旦您将表单声明为 child,命令 docmd.move 会产生一些奇怪的效果。更好的方法是使用 WinAPI 函数 SetWindowPos 来定位您的 child 窗体。坐标系将相对于 parent 形式的位置。所以0,0坐标是parent形式的top-left角。
我发现使用函数 GetWindowRect 获取 parent 表单的坐标然后设置 child 表单的位置很有用。
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() 事件中 child 表单
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
一旦您获得了主窗体的坐标,您就可以决定将 child 窗体放置在何处。最后,如果您想根据 parent 表单的大小动态调整 child 表单的位置,您可以使用 parent 表单的 form_resize() 事件来调用 child 表单的 form_resize() 事件并更改它们的大小和位置。为了做到这一点 child 形式的 form_resize() 必须声明为 public,然后你可以从任何其他形式调用它(在我们的例子中是 parent 形式).如果 parent 表单太小,您可以通过这种方式向 child 表单添加滚动条。
我希望这对任何其他对此类解决方案感兴趣的用户有所帮助。
干杯
我是这个论坛的新手,所以在此先感谢您对这个话题的帮助。
我正在尝试解决我认为是 Access Forms 的 age-old 问题。我正在开发一个数据库,其中 pop-up 主窗体打开其他几个 pop-up 窗体,我希望它们保留在主窗体的边界内。当主窗体移动或调整大小时,我需要相应地移动和调整其他打开的窗体的大小。我基本上是想让我的数据库看起来像一个主窗体有多个 child 窗体的真实应用程序。
我正在使用事件 Detail_Paint() 来检测何时更改主窗体的大小以调整其他窗体的大小,它似乎有效。但是,没有与表单屏幕上的移动相关联的事件。 MouseMove() 似乎不起作用,因为当用户在表单标题上移动鼠标光标时,它不会触发。我已经使用计时器解决了这个问题,该计时器每 10 毫秒检查一次主窗体的位置并相应地更改其他窗体的位置。但是,这会造成令人讨厌的显示器闪烁,并且还会在用户在控件中输入文本时出现问题。
我读到可以将表单子类化并将 WM_MOVING 消息捕获到 window。我为此开发了一些测试代码,但是当我尝试 运行 它时,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 Forms 的 MDI 应用程序环境。我找到了一个 API 函数,它允许将表单的 属性 设置为 child。函数是SetParent().
如果您尝试在任何访问表单的 Form_Open() 事件中使用此函数,它将将该表单视为任何其他指定表单的 child。一旦设置了 child 属性,Windows 将自动将 child 表单与主表单一起移动到屏幕上的任何位置。效果很好!
这是一个例子。
在模块中声明以下 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 位环境中工作。 然后,在childform_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
我只尝试过 pop-up 表格,所以我不知道它是否也适用于其他表格。但是,如果您将两种形式(parent 和 child)设置为 pop-up 形式,您将看到 Windows 将 child 形式与 parent 自动形成。您还可以添加 child 的 child 形式,它仍然可以很好地工作。
但是,一旦您将表单声明为 child,命令 docmd.move 会产生一些奇怪的效果。更好的方法是使用 WinAPI 函数 SetWindowPos 来定位您的 child 窗体。坐标系将相对于 parent 形式的位置。所以0,0坐标是parent形式的top-left角。
我发现使用函数 GetWindowRect 获取 parent 表单的坐标然后设置 child 表单的位置很有用。
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() 事件中 child 表单
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
一旦您获得了主窗体的坐标,您就可以决定将 child 窗体放置在何处。最后,如果您想根据 parent 表单的大小动态调整 child 表单的位置,您可以使用 parent 表单的 form_resize() 事件来调用 child 表单的 form_resize() 事件并更改它们的大小和位置。为了做到这一点 child 形式的 form_resize() 必须声明为 public,然后你可以从任何其他形式调用它(在我们的例子中是 parent 形式).如果 parent 表单太小,您可以通过这种方式向 child 表单添加滚动条。
我希望这对任何其他对此类解决方案感兴趣的用户有所帮助。
干杯