This is a question that pops up every so often on Access newsgroups. A developer
wants to draw images on a form using API functions. Unfortunately, almost all
graphics code that works on VB forms fails in Access because Access forms
- were not meant to be drawn upon, and
- they are already very heavily subclassed.
To prove these points, here's a sample that does and doesn't work.
The main issue in drawing on Access forms is working with the right hWnd and hDC.
The built in hWnd property of a form is actually bound to the form's RecordSelector
window. The client area of a form is a different window whose hWnd we have to locate
in order to draw successfully. But that's not all! To maintain that image on
the form, you have to basically redraw that image each time the window receives a
WM_PAINT message from Windows. This means that you have to subclass the window, a
technique that's
- not supported or recommended in Access 97 environment (VBA5); ( See
AddressOf) and
- although supported in Access 2000 environment (VBA6), is still not recommended.
If you run sDrawImageOnForm sub from a command button on a form,
you'll notice that the image will not get redrawn if you send the form to the background
or move another window on top of it.
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function apiGetClassName Lib "user32" _
Alias "GetClassNameA" _
(ByVal hWnd As Long, _
ByVal lpClassname As String, _
ByVal nMaxCount As Long) _
As Long
Private Declare Function apiGetWindow Lib "user32" _
Alias "GetWindow" _
(ByVal hWnd As Long, _
ByVal wCmd As Long) _
As Long
Private Declare Function apiStretchBlt Lib "gdi32" _
Alias "StretchBlt" _
(ByVal hDC As Long, _
ByVal x As Long, _
ByVal y As Long, _
ByVal nWidth As Long, _
ByVal nHeight As Long, _
ByVal hSrcDC As Long, _
ByVal xSrc As Long, _
ByVal ySrc As Long, _
ByVal nSrcWidth As Long, _
ByVal nSrcHeight As Long, _
ByVal dwRop As Long) _
As Long
Private Declare Function apiGetDC Lib "user32" _
Alias "GetDC" _
(ByVal hWnd As Long) _
As Long
Private Declare Function apiReleaseDC Lib "user32" _
Alias "ReleaseDC" _
(ByVal hWnd As Long, _
ByVal hDC As Long) _
As Long
Private Declare Function apiGetWindowRect Lib "user32" _
Alias "GetWindowRect" _
(ByVal hWnd As Long, _
lpRect As RECT) _
As Long
Private Declare Function apiSetStretchBltMode Lib "gdi32" _
Alias "SetStretchBltMode" _
(ByVal hDC As Long, _
ByVal nStretchMode As Long) _
As Long
Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2
Private Const MAX_LEN = 255
Private Const ACC_FORM_CLIENT_CLASS = "OFormSub"
Private Const ACC_FORM_CLIENT_CHILD_CLASS = "OFEDT"
Private Const SRCCOPY = &HCC0020
Private Const STRETCH_HALFTONE& = 4
Private Const STRETCH_ORSCANS& = 2
Sub sDrawImageOnForm(frm As Form)
Dim hWnd As Long, hWndSrc As Long
Dim hDCSrc As Long
Dim hDCDest As Long
Dim lpRectSrc As RECT
Dim lpRectDest As RECT
hWnd = fGetClientHandle(frm)
If hWnd = 0 Then Exit Sub
hWndSrc = hWndAccessApp
hDCSrc = apiGetDC(hWndSrc)
hDCDest = apiGetDC(hWnd)
Call apiGetWindowRect(hWndSrc, lpRectSrc)
Call apiGetWindowRect(hWnd, lpRectDest)
Call apiSetStretchBltMode(hDCDest, STRETCH_ORSCANS)
With lpRectDest
Call apiStretchBlt(hDCDest, 0, 0, .Right - .Left, .Bottom - .Top, _
hDCSrc, 0, 0, lpRectSrc.Right - lpRectSrc.Left, _
lpRectSrc.Bottom - lpRectSrc.Top, SRCCOPY)
End With
Call apiReleaseDC(hWnd, hDCDest)
Call apiReleaseDC(hWndSrc, hDCSrc)
End Sub
Function fGetClientHandle(frm As Form) As Long
Dim hWnd As Long
hWnd = apiGetWindow(frm.hWnd, GW_CHILD)
Do While hWnd
If fGetClassName(hWnd) = ACC_FORM_CLIENT_CLASS Then
If fGetClassName(apiGetWindow( _
hWnd, GW_CHILD)) = _
ACC_FORM_CLIENT_CHILD_CLASS Then
fGetClientHandle = hWnd
Exit Do
End If
End If
hWnd = apiGetWindow(hWnd, GW_HWNDNEXT)
Loop
End Function
Private Function fGetClassName(hWnd As Long) As String
Dim strBuffer As String
Dim lngCount As Long
strBuffer = String$(MAX_LEN - 1, 0)
lngCount = apiGetClassName(hWnd, strBuffer, MAX_LEN)
If lngCount > 0 Then
fGetClassName = Left$(strBuffer, lngCount)
End If
End Function
|