subclassing controls

  • Thread starter David De La PEÑA
  • Start date
D

David De La PEÑA

Hi,

I'm trying to subclass a LV in an Access Form.
First I use 'fGetClientHandle' function to get a handle to the client
window, but it doesnt work it returns 0!!!

'**************************************
Function fGetClientHandle(frm As Form) As Long
' Returns a handle to the client window of a form
' An Access form's hWnd is actually bound to the
' recordselector "window"
'

Dim hWnd As Long
Dim tmpHwnd As Long

' get the first child window of the form
hWnd = apiGetWindow(frm.hWnd, GW_CHILD)

' iterate through all child windows of the form
Do While hWnd
' if we locate the client area whose class name is "OFormSub"
If fGetClassName(hWnd) = ACC_FORM_CLIENT_CLASS Then
' the Client window's child is a window with the class
' name of OFEDT, so just verify that we're looking at the
' right window
tmpHwnd = apiGetWindow(hWnd, GW_CHILD)

If fGetClassName(tmpHwnd) = _
ACC_FORM_CLIENT_CHILD_CLASS Then
' if we found a match, then return
' the handle and we're outta here.
fGetClientHandle = hWnd
Exit Do
End If
End If
' get a handle to the next child window
hWnd = apiGetWindow(hWnd, GW_HWNDNEXT)
Loop
End Function
'**************************************

Any idea?

And after that how to get an hwnd of a control set in this form? How to
iterate throught them to get it?
 
L

Larry Linson

The Form itself, Subform Controls, and the Active Control are the only
objects that have an hWnd in Access. The other controls are just "painted on
the screen" until they become the Active Control. You can't subclass the
same way that you do in an application where every visible control is its
own window. Sorry.

But, if there's a way to do what you want, I'd think the place to start
looking for it is at MVP Stephen Lebans' site, http://www.lebans.com. He
delves deep into the Windows environment to rectify some limitations.

Larry Linson
Microsoft Access MVP

Larry Linson
Microsoft Access MVP
 
S

Stephen Lebans

David what exactly are you trying to do?
Here is some code to get you started. I modified the source to work with
Access Forms. It is from a test project where I was helping someone
Subclass a ListView control.

Place a ListView control and a TextBox control, named as indicated in
the source code onto a Form. Place the following code behind the Form.
After the code for the Form's class module you will find code to be
placed into a standard code module.
Good Luck!

Option Compare Database
Option Explicit
' demo project showing how to custom draw single items in a listview
' by Bryan Stafford of New Vision Software® - (e-mail address removed)
' this demo is released into the public domain "as is" without
' warranty or guaranty of any kind. In other words, use at
' your own risk.

' constant used to get the address of the window proc for the
subclassed window
Private Const GWL_WNDPROC As Long = (-4&)

' API call to alter the class data for this window
Private Declare Function SetWindowLong& Lib "user32" Alias
"SetWindowLongA" (ByVal hwnd&, _
ByVal
nIndex&, ByVal dwNewLong&)

' WIndow we will subclass
Private hwndTemp As Long

Private Sub Form_Load()

' we will *borrow* the font from the form since we are not using it.
' set the font attributes we want the custom draw items in the
listview to have
'­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
' With Font
' .Bold = True
' '.Italic = True
' '.Underline = True
' End With
'
' ' grab a handle to the form's font and store it in the global
variable
'Dim IFont As IFont
'Set IFont = lvCustomDraw.Object.Font
'
'g_hBoldFont = IFont.hFont
'­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
'hFont = GetStockObject(ANSI_VAR_FONT)

' fill the listview with some stuff....
With lvCustomDraw
.ColumnHeaders.Add , , "Item Column"
.ColumnHeaders.Add , , "Subitem 1"
.ColumnHeaders.Add , , "Subitem 2"

Dim i&

For i = 0 To 99
With .ListItems.Add(, , "Item " & CStr(i))
.SubItems(1) = "Subitem 1"
.SubItems(2) = "Subitem 2"
End With
Next
End With


' take control of message processing by installing our message
handling
' routine into the chain of message routines for this window.
' we subclass the form because wm_notify messages are sent to the
parent
' of the control, in this case, the VB form.

hwndTemp = FindDetailWindow(Me.hwnd)
hwndTemp = FindLVFrameWindow(hwndTemp)
g_addProcOld = SetWindowLong(hwndTemp, GWL_WNDPROC, AddressOf
WindowProcLV)

End Sub

Private Sub Form_Unload(Cancel As Integer)

' give message processing control back to VB
' if you don't do this you WILL crash!!!
Call SetWindowLong(hwndTemp, GWL_WNDPROC, g_addProcOld)

End Sub


Private Sub Text1_Click()
Me.Text1.Value = Me.lvCustomDraw.Object.hwnd
End Sub


'***********************************************************************
*****************************
' Place the following code into a standard Code module.


Option Compare Database
Option Explicit

Private Declare Function apiGetWindow Lib "user32" _
Alias "GetWindow" _
(ByVal hwnd As Long, _
ByVal wCmd As Long) _
As Long

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 FindWindow Lib "user32" Alias _
"FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long

Private Declare Function FindWindowEx Lib "user32" Alias _
"FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, _
ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

' GetWindow() Constants
Private Const GW_HWNDFIRST = 0
Private Const GW_HWNDLAST = 1
Private Const GW_HWNDNEXT = 2
Private Const GW_HWNDPREV = 3
Private Const GW_OWNER = 4
Private Const GW_CHILD = 5
Private Const GW_MAX = 5
' demo project showing how to custom draw single items in a listview
' by Bryan Stafford of New Vision Software® - (e-mail address removed)
' this demo is released into the public domain "as is" without
' warranty or guaranty of any kind. In other words, use at
' your own risk.


' Generic WM_NOTIFY notification codes for common controls
'Public Enum WinNotifications
Private Const NM_FIRST = (-0&) ' (0U- 0U) ' //
generic to all controls
Private Const NM_LAST = (-99&) ' (0U- 99U)
Private Const NM_OUTOFMEMORY = (NM_FIRST - 1&)
Private Const NM_CLICK = (NM_FIRST - 2&)
Private Const NM_DBLCLK = (NM_FIRST - 3&)
Private Const NM_RETURN = (NM_FIRST - 4&)
Private Const NM_RCLICK = (NM_FIRST - 5&)
Private Const NM_RDBLCLK = (NM_FIRST - 6&)
Private Const NM_SETFOCUS = (NM_FIRST - 7&)
Private Const NM_KILLFOCUS = (NM_FIRST - 8&)
Private Const NM_CUSTOMDRAW = (NM_FIRST - 12&)
Private Const NM_HOVER = (NM_FIRST - 13&)
'End Enum

Public Const WM_NOTIFY As Long = &H4E&

' constants used for customdraw routine
Public Const CDDS_PREPAINT As Long = &H1&
Public Const CDRF_NOTIFYITEMDRAW As Long = &H20&
Public Const CDDS_ITEM As Long = &H10000
Public Const CDDS_ITEMPREPAINT As Long = CDDS_ITEM Or CDDS_PREPAINT
Public Const CDRF_NEWFONT As Long = &H2&


' The NMHDR structure contains information about a notification
message. The pointer
' to this structure is specified as the lParam member of a WM_NOTIFY
message.
Public Type NMHDR
hwndFrom As Long ' Window handle of control sending message
idfrom As Long ' Identifier of control sending message
code As Long ' Specifies the notification code
End Type

' sub struct of the NMCUSTOMDRAW struct
' Public Type RECT
' Left As Long
' Top As Long
' Right As Long
' Bottom As Long
' End Type
'
' generic customdraw struct
Public Type NMCUSTOMDRAW
hdr As NMHDR
dwDrawStage As Long
hdc As Long
rc As RECT
dwItemSpec As Long
uItemState As Long
lItemlParam As Long
End Type

' listview specific customdraw struct
Public Type NMLVCUSTOMDRAW
nmcd As NMCUSTOMDRAW
clrText As Long
clrTextBk As Long
' if IE >= 4.0 this member of the struct can be used
'iSubItem As Integer
End Type

Public Declare Function GetStockObject Lib "gdi32" (ByVal nIndex As
Long) As Long

' function used to manipulate memory data
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory"
(lpDest As Any, lpSource As Any, ByVal cBytes&)

' gdi function used to select bold font into the hDC passed in the
prepaint message
Public Declare Function SelectObject Lib "gdi32" (ByVal hdc&, ByVal
hObject&) As Long

' handle to the bold font set for the form. this is used to set the
bold font for the listview items
Public g_hBoldFont As Long

' this var will hold a pointer to the original message handler so we
MUST
' save it so that it can be restored before we exit the app. if we
don't
' restore it.... CRASH!!!!
Public g_addProcOld As Long

' function used to call the next window proc in the "chain" for the
subclassed window
Public Declare Function CallWindowProc& Lib "user32" Alias
"CallWindowProcA" (ByVal lpPrevWndFunc&, _
ByVal hwnd&, ByVal
msg&, ByVal wparam&, ByVal lparam&)



' Stock Logical
Objects
Public Const WHITE_BRUSH = 0
Public Const LTGRAY_BRUSH = 1
Public Const GRAY_BRUSH = 2
Public Const DKGRAY_BRUSH = 3
Public Const BLACK_BRUSH = 4
Public Const NULL_BRUSH = 5
Public Const HOLLOW_BRUSH = NULL_BRUSH
Public Const WHITE_PEN = 6
Public Const BLACK_PEN = 7
Public Const NULL_PEN = 8
Public Const OEM_FIXED_FONT = 10
Public Const ANSI_FIXED_FONT = 11
Public Const ANSI_VAR_FONT = 12
Public Const SYSTEM_FONT = 13
Public Const DEVICE_DEFAULT_FONT = 14
Public Const DEFAULT_PALETTE = 15
Public Const SYSTEM_FIXED_FONT = 16
Public Const STOCK_LAST = 16

Private Const CtlMsgWinClass = "CtlFrameWork_ReflectWindow"

Public hFont As Long



'WARNING!!!! WARNING!!!! WARNING!!!! WARNING!!!! WARNING!!!! WARNING!!!!
'
' Do NOT try to step through this function in debug mode!!!!
' You WILL crash!!! Also, do NOT set any break points in this
function!!!
' You WILL crash!!! Subclassing is non-trivial and should be handled
with
' EXTREAME care!!!
'
' There are ways to use a "Debug" dll to allow you to set breakpoints in
' subclassed code in the IDE but this was not implimented for this demo.
'
'WARNING!!!! WARNING!!!! WARNING!!!! WARNING!!!! WARNING!!!! WARNING!!!!

Public Function WindowProcLV(ByVal hwnd As Long, ByVal iMsg As Long, _
ByVal wparam As Long,
ByVal lparam As Long) As Long

' this is *our* implimentation of the message handling routine


'Debug.Print "Start WIndowProc"

' determine which message was recieved
Select Case iMsg
Case WM_NOTIFY
' if it's a WM_NOTIFY message copy the data from the address
pointed to
' by lParam into a NMHDR struct
Dim udtNMHDR As NMHDR

CopyMemory udtNMHDR, ByVal lparam, 12&

With udtNMHDR
If .code = NM_CUSTOMDRAW Then
Debug.Print "Custom Draw message"
' if the code member of the struct is NM_CUSTOMDRAW, copy the
data
' pointed to by lParam into a NMLVCUSTOMDRAW struct
Dim udtNMLVCUSTOMDRAW As NMLVCUSTOMDRAW

' this is now OUR copy of the struct
CopyMemory udtNMLVCUSTOMDRAW, ByVal lparam,
Len(udtNMLVCUSTOMDRAW)

With udtNMLVCUSTOMDRAW.nmcd
' determine whether or not this is one of the messages we
are interested in
Select Case .dwDrawStage
' if it's a prepaint message, tell windows WE want first
dibs
' on painting for each item and then exit without letting
VB get this message
Case CDDS_PREPAINT
WindowProcLV = CDRF_NOTIFYITEMDRAW
Exit Function

' if it's time to paint an item, check to see if it's
divisible by 3.
' if it is, select the bold font that we borrowed from the
form into the
' hDC of the listview and set the text color to something
*different*.
' then tell windows that we changed the font for this
item.
Case CDDS_ITEMPREPAINT
Debug.Print "CDDS_ITEMPREPAINT"
If (.dwItemSpec Mod 3) = 0 Then

' We don;t need to select a new Font into the
ListView's DC
' just to change the background color of the row!!!!
'Call SelectObject(.hdc, g_hBoldFont)

' we can also set the color for items in the listview.
' we will set the color for every third bolded
item....
If (.dwItemSpec Mod 9) = 0 Then
udtNMLVCUSTOMDRAW.clrText = RGB(0, 0, 255)
udtNMLVCUSTOMDRAW.clrTextBk = RGB(255, 0, 0) ', 255,
255)
Debug.Print "new FOnt":
' copy OUR copy of the struct back to the memory
address pointed to by lParam
CopyMemory ByVal lparam, udtNMLVCUSTOMDRAW,
Len(udtNMLVCUSTOMDRAW)
End If

' tell windows that we changed the font and do not
allow VB to get this message
WindowProcLV = CDRF_NEWFONT
Exit Function
End If
End Select ' .dwDrawStage

End With ' udtNMLVCUSTOMDRAW.nmcd

End If ' .code = NM_CUSTOMDRAW

End With ' udtNMHDR

End Select ' iMsg

' pass all messages on to VB and then return the value to windows
WindowProcLV = CallWindowProc(g_addProcOld, hwnd, iMsg, wparam,
lparam)

End Function

Public Function FindDetailWindow(ByVal frmhWnd As Long) As Long
' The Detail Window is always the second of three
' windows of class OFormSub.
' 1) Form Header
' 2) Detail
' 3) Footer


Dim hWnd_VSB As Long
Dim hwnd As Long
Dim ctr As Long

ctr = 0
hwnd = frmhWnd

' Let's get first Child Window of the FORM
hWnd_VSB = apiGetWindow(hwnd, GW_CHILD)

' Let's walk through every sibling window of the Form
Do
' Thanks to Terry Kreft for explaining
' why the apiGetParent acll is not required.
' Terry is in a Class by himself! :)
'If apiGetParent(hWnd_VSB) <> hWnd Then Exit Do

If fGetClassName(hWnd_VSB) = "OFormSub" Then
ctr = ctr + 1
If ctr = 2 Then
FindDetailWindow = hWnd_VSB
Exit Function
End If

End If

' Let's get the NEXT SIBLING Window
hWnd_VSB = apiGetWindow(hWnd_VSB, GW_HWNDNEXT)

' Let's Start the process from the Top again
' Really just an error check
Loop While hWnd_VSB <> 0

' SORRY - This is impossible but there is no Detail Window!
FindDetailWindow = 0
End Function


Public Function FindLVFrameWindow(hWndDetail As Long) As Long

' The Reflector Window is a child of the Detail Section window
' find Detail Section First
'hWndMDI = FindWindowEx(Application.hWndAccessApp, 0&, "MDIClient",
vbNullString)
' Find the Debug Window
FindLVFrameWindow = FindWindowEx(hWndDetail, 0&, CtlMsgWinClass, "")


End Function

Private Function FindOKttbxWindow(ByVal frmhWnd As Long) As Long
' The Detail Window always contains
' one window of class OKttbx.

Dim hWnd_VSB As Long
Dim hwnd As Long
Dim ctr As Long

ctr = 0
hwnd = frmhWnd

' Let's get first Child Window of the FORM
hWnd_VSB = apiGetWindow(hwnd, GW_CHILD)

' Let's walk through every sibling window of the Form
Do
If fGetClassName(hWnd_VSB) = "OKttbx" Then
FindOKttbxWindow = hWnd_VSB
Exit Function
End If

' Let's get the NEXT SIBLING Window
hWnd_VSB = apiGetWindow(hWnd_VSB, GW_HWNDNEXT)

' Let's Start the process from the Top again
' Really just an error check
Loop While hWnd_VSB <> 0

' SORRY - This is impossible but there is no TextBox Window!
FindOKttbxWindow = 0
End Function

' From Dev Ashish's Site
' The Access Web
' http://www.mvps.org/access/

'******* Code Start *********
Private Function fGetClassName(hwnd As Long)
Dim strBuffer As String
Dim lngLen As Long
Const MAX_LEN = 255
strBuffer = Space$(MAX_LEN)
lngLen = apiGetClassName(hwnd, strBuffer, MAX_LEN)
If lngLen > 0 Then fGetClassName = Left$(strBuffer, lngLen)
End Function
'******* Code End *********









--

HTH
Stephen Lebans
http://www.lebans.com
Access Code, Tips and Tricks
Please respond only to the newsgroups so everyone can benefit.
 
D

David De La PEÑA

Thank you for the code!

But i would like to set the BackgroundColor of an subItem and not a
listItem. Is it posiible to do that?

And more generaly is it possible to put any common ctrl over a selected
subitem (is it possible to set selected a subitem only) as an combo... in
order to set editable the value of the subItem? And is it possible to put a
check on a column in a LV where there are boolean values?

Is there any Web site or book about subclassing (in VB if possible) and
about Windows messages to the UI and the controls?

Thank You.
 
S

Stephen Lebans

There are several VB specific sites with examples of subclassing LV and
other common controls. Start with the VB sites here:
http://www.mvps.org/
The links section at the MVP sites will lead you to several excellent VB
sites.

You should also learn how to search:
GoogleGroups:
http://groups.google.com/advanced_group_search

Code specific sites searching with tools such as:
http://www.codehound.com/vb/

Finally, and most importantly, you need to learn how to search MSDN and
navigate around this site. Here is the URL for the ListView Common
control:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc
/platform/commctls/listview/reflist.asp
And here is the URL for searching within MSDN.
http://search.microsoft.com/advanced_search.asp?siteid=us/dev

--

HTH
Stephen Lebans
http://www.lebans.com
Access Code, Tips and Tricks
Please respond only to the newsgroups so everyone can benefit.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top