Jump to content

How to use all messages in GuiGetMsg()


kcvinu
 Share

Recommended Posts

@LarsJ 

First doubt. Its related to the callback function. I have seen a function in helpfile named "_WinAPI_DefSubclassProc". And i saw that you have used DllCall for calling "DefSubclassProc" function. My doubt is - can i use the _WinAPI_DefSubclassProc " function instead of dllcall ?

2nd doubt-

I am totally confused. This is my knowledge about subclassing-

1. Write a callback function (Remember to call defWinProc inside this function)

2. Get the pointer of that function

3. Replace the defWinProc function pointer with newly created function pointer (with SetWindowLong function)

4. When the program ends call SetWindowLong to re register the old windproc function.

So this is my little knowledge. I am aproching _WinAPI_SetWindowSubclass function with my little knowledge.  I wonder what is this SubclassID ?. Your script uses 1 & 2 and help file shows 1000. And what is $pData parameter in this function ?. Helpfile uses a zero and you used a controlID of combo box. 

 

 

 

Edited by kcvinu
Spoiler

My Contributions

Glance GUI Library - A gui library based on Windows api functions. Written in Nim programming language.

UDF Link Viewer   --- A tool to visit the links of some most important UDFs 

 Includer_2  ----- A tool to type the #include statement automatically 

 Digits To Date  ----- date from 3 integer values

PrintList ----- prints arrays into console for testing.

 Alert  ------ An alternative for MsgBox 

 MousePosition ------- A simple tooltip display of mouse position

GRM Helper -------- A littile tool to help writing code with GUIRegisterMsg function

Access_UDF  -------- An UDF for working with access database files. (.*accdb only)

 

Link to comment
Share on other sites

I'll come back with an answer to your questions. In the meantime, you can look at this example. It's much more straightforward.

Link to comment
Share on other sites

Ok :)

Spoiler

My Contributions

Glance GUI Library - A gui library based on Windows api functions. Written in Nim programming language.

UDF Link Viewer   --- A tool to visit the links of some most important UDFs 

 Includer_2  ----- A tool to type the #include statement automatically 

 Digits To Date  ----- date from 3 integer values

PrintList ----- prints arrays into console for testing.

 Alert  ------ An alternative for MsgBox 

 MousePosition ------- A simple tooltip display of mouse position

GRM Helper -------- A littile tool to help writing code with GUIRegisterMsg function

Access_UDF  -------- An UDF for working with access database files. (.*accdb only)

 

Link to comment
Share on other sites

This is a few comments to the code in post 19.

I added this code based on SetWindowSubclass as an alternative to the GUIRegisterMsg function which was proposed as a solution in one of the previous posts.

As you probably already know, there are several issues related to GUIRegisterMsg:

  • A message handler created with GUIRegisterMsg is a very global message handler. All WM_COMMAND messages in the entire GUI will result in a call of the WM_COMMAND function. If you add an edit control (which generates WM_COMMAND messages) to the GUI and updates the text, the WM_COMMAND function will be called. Although only the first 4-5 lines of the function is executed because the control handle doesn't fit, it can still easily lead to performance issues.
  • GUIRegisterMsg can only register one message handler at a time for the same message type. If you have a GUI that contains a combo box and an edit control, it would be nice to be able to define a WM_COMMAND message handler for each of the controls. Because you can only define one WM_COMMAND message handler, you need to add control structures to the WM_COMMAND function to separate messages from the controls. You end up with a message handler, that contains really many code lines.
  • Especially because you can only register one GUIRegisterMsg message handler at a time for the same message type, it's often difficult to use message handlers in UDFs. This seems to restrict the creation of UDFs a little bit.

SetWindowSubclass and the associated funtions GetWindowSubclass, RemoveWindowSubclass and DefSubclassProc (all coded in WinAPIShellEx.au3) is an effective solution to all of these issues. That's just a matter of using the functions properly.

The benefits of using subclassing instead of a GUIRegisterMsg created message handler are:

  • If you create the combo box in a child window as is done in the example and subclass the child window, you can effectively limit the message handling to include messages from the combo box only.
  • You can easily create another callback function (message handler) to handle WM_COMMAND messages from eg. an edit control. You can even create different callback functions for different combo boxes.
  • Depending on the return value in the callback function, you can decide whether the WM_COMMAND notification is forwarded to another message handler. In the example the return value is 1 for all WM_COMMAND notifications, which prevents the notifications from being forwarded.
  • It's easy to implement a message handler based on subclassing in a UDF.
  • The user can create her own message handler with GUIRegisterMsg completely as usual.

The function SetWindowLong should not be used for subclassing. MicroSoft themselves have documented the reasons.

I'll come back with an answer to your questions in post 21.

Link to comment
Share on other sites

31 minutes ago, LarsJ said:

You can even create different callback functions for different combo boxes

Can you create a single callback for multiple controls, for example a group of checkboxes?

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

Yes, You can create a single callback function for a group of checkboxes. And then you can use the control handle to separate messages from different controls, if you need that. This is exactly the same as you would do in a message handler created with GUIRegisterMsg.

Notifications from a checkbox are send as WM_COMMAND messages. Now we have a checkbox, a combobox and an edit control which all sends notifications as WM_COMMAND messages. With GUIRegisterMsg you can only create one big function to handle all these WM_COMMAND messages. It's all mixed together in one big hotchpotch, if one can put it like that.

With subclassing you can create three small functions which all can handle WM_COMMAND messages. A small function to handle WM_COMMAND messages from the checkbox or a group of checkboxes. A small function to handle WM_COMMAND messages from the combobox or a group of comboboxes. And a small function to handle WM_COMMAND messages from the edit control or a group of edit controls. This is one of the major advantages of subclassing.

Link to comment
Share on other sites

kcvinu, I'll answer your 2nd doubt (post 21) first. The four points you have listet, is the technique used for the old approach based on SetWindowLong function. Subclassing based on the new function SetWindowSubclass is much easier. You more or less use the same technique as you use to create a message handler with GUIRegisterMsg.

This is a slightly modified version of the example for _GUICtrlComboBox_Create in the help file:

#include <GuiComboBox.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Global $g_hCombo

Example()


Func Example()
  ; Create GUI
  Local $hGui = GUICreate("(UDF) ComboBox Create", 400, 300)
  $g_hCombo = _GUICtrlComboBox_Create($hGui, "", 2, 2, 396, 296)

  ; Add files
  _GUICtrlComboBox_BeginUpdate($g_hCombo)
  _GUICtrlComboBox_AddDir($g_hCombo, "", $DDL_DRIVES, False)
  _GUICtrlComboBox_EndUpdate($g_hCombo)

  ; Register WM_COMMAND message handler
  GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

  ; Show GUI
  GUISetState(@SW_SHOW)

  ; Message loop
  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  GUIRegisterMsg($WM_COMMAND, "") ; Unregister WM_COMMAND message handler
  GUIDelete()
EndFunc

; WM_COMMAND message handler
Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $g_hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "$CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "$CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "$CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "$CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "$CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "$CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "$CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "$CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "$CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "$CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg
EndFunc

 

And this is the same WM_COMMAND message handler implemented through SetWindowSubclass:

#include <GuiComboBox.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIShellEx.au3>

Global $g_hCombo

Example()


Func Example()
  ; Create GUI
  Local $hGui = GUICreate("(UDF) ComboBox Create", 400, 300)
  $g_hCombo = _GUICtrlComboBox_Create($hGui, "", 2, 2, 396, 296)

  ; Add files
  _GUICtrlComboBox_BeginUpdate($g_hCombo)
  _GUICtrlComboBox_AddDir($g_hCombo, "", $DDL_DRIVES, False)
  _GUICtrlComboBox_EndUpdate($g_hCombo)

  ; Register message handler based on subclassing
  Local $pMsgHandler = DllCallbackGetPtr( DllCallbackRegister( "MsgHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
  _WinAPI_SetWindowSubclass( $hGui, $pMsgHandler, 1000, 0 ) ; $iSubclassId = 1000, $pData = 0

  ; Show GUI
  GUISetState(@SW_SHOW)

  ; Message loop
  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  _WinAPI_RemoveWindowSubclass( $hGui, $pMsgHandler, 1000 ) ; Unregister message handler
  GUIDelete()
EndFunc

; Message handler based on subclassing
Func MsgHandler( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData )
  If $iMsg <> $WM_COMMAND Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  ; Now it's a WM_COMMAND message handler (all other messages are filtered away in the line above)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $g_hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "$CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "$CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "$CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "$CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "$CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "$CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "$CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "$CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "$CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "$CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
      EndSwitch
  EndSwitch

  ; Call next function in subclass chain (this forwards WM_COMMAND messages to main GUI (other messages are already forwarded))
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $hWnd, $iMsg, $iSubclassId, $pData
EndFunc

Note that the entire GUI is subclassed. Enter some text in the edit box to generate a lot of WM_COMMAND messages.

Note also that the two code sections are almost identical.

These lines

; Register message handler based on subclassing
Local $pMsgHandler = DllCallbackGetPtr( DllCallbackRegister( "MsgHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
_WinAPI_SetWindowSubclass( $hGui, $pMsgHandler, 1000, 0 ) ; $iSubclassId = 1000, $pData = 0

replaces these lines:

; Register WM_COMMAND message handler
GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

This line

_WinAPI_RemoveWindowSubclass( $hGui, $pMsgHandler, 1000 ) ; Unregister message handler

replaces this line:

GUIRegisterMsg($WM_COMMAND, "") ; Unregister WM_COMMAND message handler

 

The two message handler functions are more or less identical.

In this simple case the message handler based on GUIRegisterMsg is much much better than the message handler based on SetWindowSubclass. Why is the first implementation so much better? This is important to know.
 

Schematically the message flow looks like this in the two situations. GUIRegisterMsg to the left and subclassing to the right:
Subclassing_zpsz33stoyp.png

Edited by LarsJ
Link to comment
Share on other sites

Now we can try to implement both methods in the same script:

#include <GuiComboBox.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIShellEx.au3>

Global $g_hCombo

Example()


Func Example()
  ; Create GUI
  Local $hGui = GUICreate("(UDF) ComboBox Create", 400, 300)
  $g_hCombo = _GUICtrlComboBox_Create($hGui, "", 2, 2, 396, 296)

  ; Add files
  _GUICtrlComboBox_BeginUpdate($g_hCombo)
  _GUICtrlComboBox_AddDir($g_hCombo, "", $DDL_DRIVES, False)
  _GUICtrlComboBox_EndUpdate($g_hCombo)

  ; Register message handler based on subclassing
  Local $pMsgHandler = DllCallbackGetPtr( DllCallbackRegister( "MsgHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
  _WinAPI_SetWindowSubclass( $hGui, $pMsgHandler, 1000, 0 ) ; $iSubclassId = 1000, $pData = 0

  ; Register WM_COMMAND message handler
  GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

  ; Show GUI
  GUISetState(@SW_SHOW)

  ; Message loop
  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  GUIRegisterMsg($WM_COMMAND, "") ; Unregister WM_COMMAND message handler
  _WinAPI_RemoveWindowSubclass( $hGui, $pMsgHandler, 1000 ) ; Unregister message handler
  GUIDelete()
EndFunc

; Message handler based on subclassing
Func MsgHandler( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData )
  If $iMsg <> $WM_COMMAND Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  ; Now it's a WM_COMMAND message handler (all other messages are filtered away in the line above)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $g_hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "MsgHandler: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "MsgHandler: $CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "MsgHandler: $CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "MsgHandler: $CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "MsgHandler: $CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "MsgHandler: $CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "MsgHandler: $CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "MsgHandler: $CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "MsgHandler: $CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "MsgHandler: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
      EndSwitch
  EndSwitch

  ; Call next function in subclass chain (this forwards WM_COMMAND messages to main GUI (other messages are already forwarded))
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $hWnd, $iMsg, $iSubclassId, $pData
EndFunc

; WM_COMMAND message handler
Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $g_hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "WM_COMMAND: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "WM_COMMAND: $CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "WM_COMMAND: $CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "WM_COMMAND: $CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "WM_COMMAND: $CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "WM_COMMAND: $CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "WM_COMMAND: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg
EndFunc

 

And we can add a button to turn subclassing on/off on the fly:

#include <GuiComboBox.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIShellEx.au3>

Global $g_hCombo

Example()


Func Example()
  ; Create GUI
  Local $hGui = GUICreate("(UDF) ComboBox Create", 400, 300)
  Local $idButton = GUICtrlCreateButton( "Turn subclassing off", 2, 2, 396, 25 ), $bsubclassingEnabled = True
  GUICtrlSetBkColor( $idButton, 0xCCFFCC )
  $g_hCombo = _GUICtrlComboBox_Create($hGui, "", 2, 40, 396, 296)

  ; Add files
  _GUICtrlComboBox_BeginUpdate($g_hCombo)
  _GUICtrlComboBox_AddDir($g_hCombo, "", $DDL_DRIVES, False)
  _GUICtrlComboBox_EndUpdate($g_hCombo)

  ; Register message handler based on subclassing
  Local $pMsgHandler = DllCallbackGetPtr( DllCallbackRegister( "MsgHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
  _WinAPI_SetWindowSubclass( $hGui, $pMsgHandler, 1000, 0 ) ; $iSubclassId = 1000, $pData = 0

  ; Register WM_COMMAND message handler
  GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

  ; Show GUI
  GUISetState(@SW_SHOW)

  ; Message loop
  While 1
    Switch GUIGetMsg()
      Case $idButton
        If $bsubclassingEnabled Then
          GUICtrlSetBkColor( $idButton, 0xFFCCCC )
          GUICtrlSetData( $idButton, "Turn subclassing on" )
          _WinAPI_RemoveWindowSubclass( $hGui, $pMsgHandler, 1000 ) ; Unregister message handler
        Else
          GUICtrlSetBkColor( $idButton, 0xCCFFCC )
          GUICtrlSetData( $idButton, "Turn subclassing off" )
          _WinAPI_SetWindowSubclass( $hGui, $pMsgHandler, 1000, 0 ) ; $iSubclassId = 1000, $pData = 0
        EndIf
        $bsubclassingEnabled = Not $bsubclassingEnabled

      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  GUIRegisterMsg($WM_COMMAND, "") ; Unregister WM_COMMAND message handler
  _WinAPI_RemoveWindowSubclass( $hGui, $pMsgHandler, 1000 ) ; Unregister message handler
  GUIDelete()
EndFunc

; Message handler based on subclassing
Func MsgHandler( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData )
  If $iMsg <> $WM_COMMAND Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  ; Now it's a WM_COMMAND message handler (all other messages are filtered away in the line above)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $g_hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "MsgHandler: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "MsgHandler: $CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "MsgHandler: $CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "MsgHandler: $CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "MsgHandler: $CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "MsgHandler: $CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "MsgHandler: $CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "MsgHandler: $CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "MsgHandler: $CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "MsgHandler: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          Return 1
      EndSwitch
  EndSwitch

  ; Call next function in subclass chain (this forwards WM_COMMAND messages to main GUI (other messages are already forwarded))
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $hWnd, $iMsg, $iSubclassId, $pData
EndFunc

; WM_COMMAND message handler
Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $g_hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "WM_COMMAND: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "WM_COMMAND: $CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "WM_COMMAND: $CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "WM_COMMAND: $CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "WM_COMMAND: $CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "WM_COMMAND: $CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "WM_COMMAND: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg
EndFunc

Note that I've replaced "; no return value" with "Return 1" in MsgHandler function.

Link to comment
Share on other sites

Finally we can implement the subclassing functions in a small UDF:

Save as Subclassing.au3:

#include-once
#include <GuiComboBox.au3>
#include <WindowsConstants.au3>
#include <WinAPIShellEx.au3>

; Register message handler callback function
Global $pMsgHandler = DllCallbackGetPtr( DllCallbackRegister( "MsgHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )

Func SubclassingInit( $hParent, $hCombo )
  ; Register message handler based on subclassing
  _WinAPI_SetWindowSubclass( $hParent, $pMsgHandler, 1000, $hCombo ) ; $iSubclassId = 1000, $pData = $hCombo
EndFunc

Func SubclassingExit( $hParent )
  _WinAPI_RemoveWindowSubclass( $hParent, $pMsgHandler, 1000 ) ; Unregister message handler
EndFunc

; Message handler based on subclassing
Func MsgHandler( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $hCombo ) ; $pData = $hCombo
  If $iMsg <> $WM_COMMAND Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  ; Now it's a WM_COMMAND message handler (all other messages are filtered away in the line above)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $hCombo ; $pData = $hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "MsgHandler: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "MsgHandler: $CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "MsgHandler: $CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "MsgHandler: $CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "MsgHandler: $CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "MsgHandler: $CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "MsgHandler: $CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "MsgHandler: $CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "MsgHandler: $CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          Return 1
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "MsgHandler: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          Return 1
      EndSwitch
  EndSwitch

  ; Call next function in subclass chain (this forwards WM_COMMAND messages to main GUI (other messages are already forwarded))
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $hWnd, $iMsg, $iSubclassId
EndFunc

 

#include <GUIConstantsEx.au3>
#include "Subclassing.au3"

Global $g_hCombo

Example()


Func Example()
  ; Create GUI
  Local $hGui = GUICreate("(UDF) ComboBox Create", 400, 300)
  Local $idButton = GUICtrlCreateButton( "Turn subclassing off", 2, 2, 396, 25 ), $bsubclassingEnabled = True
  GUICtrlSetBkColor( $idButton, 0xCCFFCC )
  $g_hCombo = _GUICtrlComboBox_Create($hGui, "", 2, 40, 396, 296)

  ; Add files
  _GUICtrlComboBox_BeginUpdate($g_hCombo)
  _GUICtrlComboBox_AddDir($g_hCombo, "", $DDL_DRIVES, False)
  _GUICtrlComboBox_EndUpdate($g_hCombo)

  ; Register message handler based on subclassing
  SubclassingInit( $hGui, $g_hCombo )

  ; Register WM_COMMAND message handler
  GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

  ; Show GUI
  GUISetState(@SW_SHOW)

  ; Message loop
  While 1
    Switch GUIGetMsg()
      Case $idButton
        If $bsubclassingEnabled Then
          GUICtrlSetBkColor( $idButton, 0xFFCCCC )
          GUICtrlSetData( $idButton, "Turn subclassing on" )
          SubclassingExit( $hGui ) ; Unregister message handler
        Else
          GUICtrlSetBkColor( $idButton, 0xCCFFCC )
          GUICtrlSetData( $idButton, "Turn subclassing off" )
          SubclassingInit( $hGui, $g_hCombo ) ; Register message handler
        EndIf
        $bsubclassingEnabled = Not $bsubclassingEnabled

      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  GUIRegisterMsg($WM_COMMAND, "") ; Unregister WM_COMMAND message handler
  SubclassingExit( $hGui ) ; Unregister message handler
  GUIDelete()
EndFunc

; WM_COMMAND message handler
Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
  Local static $iCount = 0
  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; Hi Word
  $iCount += 1
  Switch $hWndFrom
    Case $g_hCombo
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          ConsoleWrite( "WM_COMMAND: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_DBLCLK       (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
          ConsoleWrite( "WM_COMMAND: $CBN_DROPDOWN     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_EDITCHANGE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
          ConsoleWrite( "WM_COMMAND: $CBN_EDITUPDATE   (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
          ConsoleWrite( "WM_COMMAND: $CBN_KILLFOCUS    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
          ConsoleWrite( "WM_COMMAND: $CBN_SELCHANGE    (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
          ConsoleWrite( "WM_COMMAND: $CBN_SELENDCANCEL (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
          ConsoleWrite( "WM_COMMAND: $CBN_SELENDOK     (" & $iCount & ")" & @CRLF )
          ; no return value
        Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
          ConsoleWrite( "WM_COMMAND: $CBN_CLOSEUP      (" & $iCount & ")" & @CRLF )
          ; no return value
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg
EndFunc

 

Link to comment
Share on other sites

@LarsJ Wow !. This seems to be a complete tutorial on that subject. Thank you. Let me read this. :) :D

Spoiler

My Contributions

Glance GUI Library - A gui library based on Windows api functions. Written in Nim programming language.

UDF Link Viewer   --- A tool to visit the links of some most important UDFs 

 Includer_2  ----- A tool to type the #include statement automatically 

 Digits To Date  ----- date from 3 integer values

PrintList ----- prints arrays into console for testing.

 Alert  ------ An alternative for MsgBox 

 MousePosition ------- A simple tooltip display of mouse position

GRM Helper -------- A littile tool to help writing code with GUIRegisterMsg function

Access_UDF  -------- An UDF for working with access database files. (.*accdb only)

 

Link to comment
Share on other sites

You are welcome, you master of subclassing. Don't miss the question above the picture in post 27.

Link to comment
Share on other sites

@LarsJ Read it in one strech. After reading, i have this assumption now

1. Use GuiRegisterMsg if you don't want to control the features on the fly. Otherwise you can use SetWindowSubclassing.

2. Without the above said difference, both methods are identical 

Now these are the doubts that remained un cleared. 

1. Can i use _WinAPI_DefSubclassProc function instead of calling "comctl32.dll" ?

2. You stated that when using SetWindowSubclassing, you can make it as a UDF. I wonder, that how can i control the response to each notifications or messages. In this example you are one who wrote the MsgHandler callback function. So if in need to change the behaviour, i need to edit your MsgHandler function. Is that a disadvantage ?

 

Spoiler

My Contributions

Glance GUI Library - A gui library based on Windows api functions. Written in Nim programming language.

UDF Link Viewer   --- A tool to visit the links of some most important UDFs 

 Includer_2  ----- A tool to type the #include statement automatically 

 Digits To Date  ----- date from 3 integer values

PrintList ----- prints arrays into console for testing.

 Alert  ------ An alternative for MsgBox 

 MousePosition ------- A simple tooltip display of mouse position

GRM Helper -------- A littile tool to help writing code with GUIRegisterMsg function

Access_UDF  -------- An UDF for working with access database files. (.*accdb only)

 

Link to comment
Share on other sites

I guess you mean this -

In GuiRegisterNMsg method, AutoIt MsgHandler is above the WM_COMMAND msg handler. That means the msgs dispatched from OS's msg queue is interepted by autoit internal msg handler. So this makes a perfomance lag.

But i Subclass method,  autoit internal msg handler is last one. So it don't interept any other msgs and hence there is no perfomance lag.

Am i right ? 

Spoiler

My Contributions

Glance GUI Library - A gui library based on Windows api functions. Written in Nim programming language.

UDF Link Viewer   --- A tool to visit the links of some most important UDFs 

 Includer_2  ----- A tool to type the #include statement automatically 

 Digits To Date  ----- date from 3 integer values

PrintList ----- prints arrays into console for testing.

 Alert  ------ An alternative for MsgBox 

 MousePosition ------- A simple tooltip display of mouse position

GRM Helper -------- A littile tool to help writing code with GUIRegisterMsg function

Access_UDF  -------- An UDF for working with access database files. (.*accdb only)

 

Link to comment
Share on other sites

No, I do not think the answer is good enough. And your assumptions are not all that good. Test the code more careful, watch the output in SciTE console, you can add your own ConsoleWrite statements, think more careful about the differences and the consequences of the differences between the two flow charts. What are the consequences of subclassing the entire GUI? What can you learn from the example about turning subclassing on/off? What happens if you comment out the first and the second last line in MsgHandler function. And why is this happening? Why do you think yourself that I execute the DllCall function directly instead of using _WinAPI_DefSubclassProc? You'll get 24 hours to test the code and think about it. I think that everyone is welcome to answer.

Link to comment
Share on other sites

Hi Larsj,

I think that time is not enough for me to find the answers of these questions. I will sure check it try to find answers as soon as i have got some free time. 

Spoiler

My Contributions

Glance GUI Library - A gui library based on Windows api functions. Written in Nim programming language.

UDF Link Viewer   --- A tool to visit the links of some most important UDFs 

 Includer_2  ----- A tool to type the #include statement automatically 

 Digits To Date  ----- date from 3 integer values

PrintList ----- prints arrays into console for testing.

 Alert  ------ An alternative for MsgBox 

 MousePosition ------- A simple tooltip display of mouse position

GRM Helper -------- A littile tool to help writing code with GUIRegisterMsg function

Access_UDF  -------- An UDF for working with access database files. (.*accdb only)

 

Link to comment
Share on other sites

I have given some credit to kcvinu and JohnOne for post 20 and 25. Without these entries this rather interesting development of the thread was probably not happened.

And thank you for all the feedback. Always nice with some feedback.

I'll add two new examples. In these examples I'll replace the combobox with my favorite control number one: The listview. In a listview notifications are not send as WM_COMMAND messages but as WM_NOTIFY messages.

This means that the callback function will look like this:

; Message handler based on subclassing
Func MsgHandler( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData )
  If $iMsg <> $WM_NOTIFY Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  ; Now it's a WM_NOTIFY message handler (all other messages are filtered away in the line above)
  ; Code for the WM_NOTIFY message handler

  ; Call next function in subclass chain (this forwards WM_NOTIFY messages to main GUI (other messages are already forwarded))
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $hWnd, $iMsg, $iSubclassId, $pData
EndFunc

A few notes about subclassing
Subclassing is about Windows messages. The purpose of subclassing is to capture messages to a window or control before they are received by the original receiver. In that way you can respond to messages with your own code instead of the default code. In our case with a listview, we can for example respond to custom draw notifications (NM_CUSTOMDRAW) for the listview by drawing our own colors instead of the default white and black back and fore colors. Because we only handles custom draw notifications, all other notifications and messages must be forwarded to the original receiver.

What are we going to subclass?
We are going to subclass the window or control that receives the WM_NOTIFY messages that contains the custom draw notifications. These messages are sent by the code in ComCtl32.dll and by the operating system. The messages are not sent to the listview, but to the parent window of the listview. The parent of a listview is usually an AutoIt GUI.

The subclass callback function
The subclass callback function is simply a message handler. We register the callback function with DllCallbackRegister and gets a pointer for the function with DllCallbackGetPtr.

The second codebox in post 27 shows that the procedure for implementing a message handler based on _WinAPI_SetWindowSubclass is very similar to the procedure for implementing a message handler created with GUIRegisterMsg.

This is an updated version of the flow chart in post 27. GUIRegisterMsg to the left. _WinAPI_SetWindowSubclass to the right.
Subclassing-35_zpsmqkj98sd.png

The left chart (GUIRegisterMsg)
WM_MESSAGEs from Windows OS is generated in DLL-files in response to user actions in the GUI. For example WM_MOUSEMOVE messages when the user moves the mouse across the GUI from one control to another. A lot of WM_MOUSEMOVE messages. The code in DLL-files is compiled C++ code. This is very fast code. The code can easily generate all the messages.

The messages are sent to the internal AutoIt message handler. The code in this message handler is also compiled C++ code. The code can easily handle all the messages.

In case the message is a WM_NOTIFY message and we have registered a WM_NOTIFY message handler with GUIRegisterMsg, the internal message handler forwards the message to the WM_NOTIFY function (or whatever you have called the function). Only WM_NOTIFY messages are forwarded.

The right chart (_WinAPI_SetWindowSubclass)
If you implement subclassing in your code, the callback message handler is always inserted at this point in the message flow as shown in the right chart. The code in this message handler is AutoIt code. Compared to compiled C++ code AutoIt code is a little slow. In a very round number about 1,000 times slower. Inserting AutoIt code at that point of the message flow is a very potential bottleneck. This is the most important thing to remember if you implement subclassing in your code.

If you subclass the entire GUI every single message that is generated will result in a call of the callback function. For example all the WM_MOUSEMOVE messages. Even if you are only interested in WM_NOTIFY messages, filtering away all the other messages is still done in AutoIt code. In the chart to the left this is done in C++ code.

When you implement subclassing you'll typically subclass for example the GUI several times. This means that a series of callback functions is inserted at that point of the message flow. All the functions are called once for each single message that is generated in the entire GUI. Not just WM_NOTIFY messages. The whole bunch of messages.

Filtering away all the uninteresting messages must be absolutely fast. That's why I've chosen to execute the DllCall directly (the first line in the code above).

If you are subclassing listviews and header controls don't forget this nasty little issue.

I'll be back in an hour or two with the first example.

Link to comment
Share on other sites

In this example 4 listviews are created directly in the GUI. The GUI is subclassed 4 times. One time for each listview.

Here is the code. I've included the code in the zip below.

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIShellEx.au3>
#include "GuiListViewEx.au3"

Opt( "MustDeclareVars", 1 )
Opt( "GUIResizeMode", $GUI_DOCKALL )

Global $hListView1, $hListView2, $hListView3, $hListView4, $bSubclass, $bAllMsgs = True, $nMsg = 0

Example()


Func Example()
  ; Create GUI
  Local $hGui = GUICreate( "Custom draw, GUI", 830, 420 )

  ; ------------------------ ListView 1 ------------------------

  GUICtrlCreateLabel( "ListView 1", 350, 25, 60, 15 )
  Local $idBut1 = GUICtrlCreateButton( "Turn subclassing off", 10, 10, 150, 25 ), $bEnabled1 = True
  GUICtrlSetBkColor( $idBut1, 0xCCFFCC )

  ; Create ListView
  Local $idListView1 = GUICtrlCreateListView( "", 10, 40, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView1 = GUICtrlGetHandle( $idListView1 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView1, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView1, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView1, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView1, "Column 4", 94 )

  ; Fill ListView
  Local $iItems1 = 3
  For $i = 0 To $iItems1 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView1 )
  Next

  ; Adjust height of GUI and ListView to fit ten rows
  Local $iLvHeight = _GUICtrlListView_GetHeightToFitRows( $hListView1, 10 )
  WinMove( $hGui, "", Default, Default, Default, WinGetPos( $hGui )[3] - WinGetClientSize( $hGui )[1] + 2*$iLvHeight + 30 + 60 + 35 )
  WinMove( $hListView1, "", 10, 40, Default, $iLvHeight )

  ; ------------------------ ListView 2 ------------------------

  GUICtrlCreateLabel( "ListView 2", 760, 25, 60, 15 )
  Local $idBut2 = GUICtrlCreateButton( "Turn subclassing off", 420, 10, 150, 25 ), $bEnabled2 = True
  GUICtrlSetBkColor( $idBut2, 0xCCFFCC )

  ; Create ListView
  Local $idListView2 = GUICtrlCreateListView( "", 420, 40, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView2 = GUICtrlGetHandle( $idListView2 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView2, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView2, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView2, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView2, "Column 4", 94 )

  ; Fill ListView
  Local $iItems2 = 3
  For $i = 0 To $iItems2 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView2 )
  Next

  ; Adjust height of ListView to fit ten rows
  WinMove( $hListView2, "", 420, 40, Default, $iLvHeight )

  ; ------------------------ ListView 3 ------------------------

  GUICtrlCreateLabel( "ListView 3", 350, 20+60+$iLvHeight-15, 60, 15 )
  Local $idBut3 = GUICtrlCreateButton( "Turn subclassing off", 10, 20+30+$iLvHeight, 150, 25 ), $bEnabled3 = True
  GUICtrlSetBkColor( $idBut3, 0xCCFFCC )

  ; Create ListView
  Local $idListView3 = GUICtrlCreateListView( "", 10, 20+60+$iLvHeight, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView3 = GUICtrlGetHandle( $idListView3 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView3, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView3, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView3, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView3, "Column 4", 94 )

  ; Fill ListView
  Local $iItems3 = 3
  For $i = 0 To $iItems3 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView3 )
  Next

  ; Adjust height of ListView to fit ten rows
  WinMove( $hListView3, "", 10, 20+60+$iLvHeight, Default, $iLvHeight )

  ; ------------------------ ListView 4 ------------------------

  GUICtrlCreateLabel( "ListView 4", 760, 20+60+$iLvHeight-15, 60, 15 )
  Local $idBut4 = GUICtrlCreateButton( "Turn subclassing off", 420, 20+30+$iLvHeight, 150, 25 ), $bEnabled4 = True
  GUICtrlSetBkColor( $idBut4, 0xCCFFCC )

  ; Create ListView
  Local $idListView4 = GUICtrlCreateListView( "", 420, 20+60+$iLvHeight, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView4 = GUICtrlGetHandle( $idListView4 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView4, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView4, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView4, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView4, "Column 4", 94 )

  ; Fill ListView
  Local $iItems4 = 3
  For $i = 0 To $iItems4 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView4 )
  Next

  ; Adjust height of ListView to fit ten rows
  WinMove( $hListView4, "", 420, 20+60+$iLvHeight, Default, $iLvHeight )

  ; ------------------------------------------------------------

  ; Create some controls
                    GUICtrlCreateButton( "Button",                10, 20+60+2*$iLvHeight+10,   150, 25 )
                    GUICtrlCreateLabel ( "WindowProc function:", 450, 20+60+2*$iLvHeight+10+5, 130, 15 )
  Local $idRadio1 = GUICtrlCreateRadio ( "All messages",         580, 20+60+2*$iLvHeight+10,    90, 25 ), $bRadio1Checked = True
  Local $idRadio2 = GUICtrlCreateRadio ( "WM_NOTIFY messages",   680, 20+60+2*$iLvHeight+10,   140, 25 )
  GUICtrlSetState( $idRadio1, $GUI_CHECKED )

  ; Register callback function to subclass main window
  Local $pWindowProc = DllCallbackGetPtr( DllCallbackRegister( "WindowProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )

  ; Subclass GUI (ListView 1)
  _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 1, $hListView1 ) ; SubclassId = 1, $pData = $hListView1

  ; Subclass GUI (ListView 2)
  _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 2, $hListView2 ) ; SubclassId = 2, $pData = $hListView2

  ; Subclass GUI (ListView 3)
  _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 3, $hListView3 ) ; SubclassId = 3, $pData = $hListView3

  ; Subclass GUI (ListView 4)
  _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 4, $hListView4 ) ; SubclassId = 4, $pData = $hListView4

  ; Register WM_NOTIFY message handler
  GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )

  ; Show GUI
  GUISetState( @SW_SHOW )

  ; Message loop
  While 1
    Switch GUIGetMsg()
      Case $idBut1
        If $bEnabled1 Then
          GUICtrlSetData( $idBut1, "Turn subclassing on" )
          _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 1 )
          GUICtrlSetBkColor( $idBut1, 0xFFCCCC )
        Else
          GUICtrlSetData( $idBut1, "Turn subclassing off" )
          _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 1, $hListView1 ) ; SubclassId = 1, $pData = $hListView1
          GUICtrlSetBkColor( $idBut1, 0xCCFFCC )
        EndIf
        $bEnabled1 = Not $bEnabled1

      Case $idBut2
        If $bEnabled2 Then
          GUICtrlSetData( $idBut2, "Turn subclassing on" )
          _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 2 )
          GUICtrlSetBkColor( $idBut2, 0xFFCCCC )
        Else
          GUICtrlSetData( $idBut2, "Turn subclassing off" )
          _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 2, $hListView2 ) ; SubclassId = 2, $pData = $hListView2
          GUICtrlSetBkColor( $idBut2, 0xCCFFCC )
        EndIf
        $bEnabled2 = Not $bEnabled2

      Case $idBut3
        If $bEnabled3 Then
          GUICtrlSetData( $idBut3, "Turn subclassing on" )
          _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 3 )
          GUICtrlSetBkColor( $idBut3, 0xFFCCCC )
        Else
          GUICtrlSetData( $idBut3, "Turn subclassing off" )
          _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 3, $hListView3 ) ; SubclassId = 3, $pData = $hListView3
          GUICtrlSetBkColor( $idBut3, 0xCCFFCC )
        EndIf
        $bEnabled3 = Not $bEnabled3

      Case $idBut4
        If $bEnabled4 Then
          GUICtrlSetData( $idBut4, "Turn subclassing on" )
          _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 4 )
          GUICtrlSetBkColor( $idBut4, 0xFFCCCC )
        Else
          GUICtrlSetData( $idBut4, "Turn subclassing off" )
          _WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 4, $hListView4 ) ; SubclassId = 4, $pData = $hListView4
          GUICtrlSetBkColor( $idBut4, 0xCCFFCC )
        EndIf
        $bEnabled4 = Not $bEnabled4

      Case $idRadio1, $idRadio2
        GUICtrlSetState( $idRadio1, $bRadio1Checked ? $GUI_UNCHECKED : $GUI_CHECKED )
        GUICtrlSetState( $idRadio2, $bRadio1Checked ? $GUI_CHECKED : $GUI_UNCHECKED )
        $bRadio1Checked = Not $bRadio1Checked
        $bAllMsgs = $bRadio1Checked

      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 1 )
  _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 2 )
  _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 3 )
  _WinAPI_RemoveWindowSubclass( $hGui, $pWindowProc, 4 )
  GUIDelete()
EndFunc

; WindowProc callback function
Func WindowProc( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $hListView )
  $bSubclass = True

  If $bAllMsgs Then
    $nMsg += 1
    Switch $hListView
      Case $hListView1
        ConsoleWrite( "WindowProc function: ListView 1 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView2
        ConsoleWrite( "WindowProc function: ListView 2 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView3
        ConsoleWrite( "WindowProc function: ListView 3 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView4
        ConsoleWrite( "WindowProc function: ListView 4 " & "(" & $nMsg & ")" & @CRLF )
    EndSwitch
  EndIf

  If $iMsg <> $WM_NOTIFY Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  If Not $bAllMsgs Then
    $nMsg += 1
    Switch $hListView
      Case $hListView1
        ConsoleWrite( "WindowProc function: ListView 1 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView2
        ConsoleWrite( "WindowProc function: ListView 2 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView3
        ConsoleWrite( "WindowProc function: ListView 3 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView4
        ConsoleWrite( "WindowProc function: ListView 4 " & "(" & $nMsg & ")" & @CRLF )
    EndSwitch
  EndIf

  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
  Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) ) ; $hWndFrom
    Case $hListView
      Switch DllStructGetData( $tNMHDR, "Code" ) ; $iCode
        Case $NM_CUSTOMDRAW
          Local $tNMLVCustomDraw = DllStructCreate( $tagNMLVCUSTOMDRAW, $lParam )
          Local $dwDrawStage = DllStructGetData( $tNMLVCustomDraw, "dwDrawStage" )
          Switch $dwDrawStage                    ; Specifies the drawing stage
            ; Stage 1
            Case $CDDS_PREPAINT                  ; Before the paint cycle begins
              ConsoleWrite( "WindowProc Stage 1: CDDS_PREPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NOTIFYITEMDRAW + _    ; Stage 2 will be carried out
                     $CDRF_NOTIFYPOSTPAINT       ; Stage 6 will be carried out
              Return $CDRF_NOTIFYITEMDRAW        ; Notify the parent window before an item is painted
              Return $CDRF_NOTIFYPOSTPAINT       ; Notify the parent window after the paint cycle is complete

            ; Stage 2
            Case $CDDS_ITEMPREPAINT              ; Before an item is painted
              ConsoleWrite( "WindowProc Stage 2: CDDS_ITEMPREPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NOTIFYSUBITEMDRAW + _ ; Stage 3 will be carried out
                     $CDRF_NOTIFYPOSTPAINT       ; Stage 5 will be carried out
              Return $CDRF_NOTIFYSUBITEMDRAW     ; Notify the parent window before a subitem is painted
              Return $CDRF_NOTIFYPOSTPAINT       ; Notify the parent window after an item is painted

            ; Stage 3
            Case BitOR( $CDDS_ITEMPREPAINT, _
                        $CDDS_SUBITEM )          ; Before a subitem is painted
              ConsoleWrite( "WindowProc Stage 3: CDDS_ITEMPREPAINT,  CDDS_SUBITEM " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NOTIFYPOSTPAINT       ; Stage 4 will be carried out
              Return $CDRF_NOTIFYPOSTPAINT       ; Notify the parent window after a subitem is painted

            ; Stage 4
            Case BitOR( $CDDS_ITEMPOSTPAINT, _
                        $CDDS_SUBITEM )          ; After a subitem has been painted
              ConsoleWrite( "WindowProc Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NEWFONT               ; $CDRF_NEWFONT must be returned after changing font or colors

            ; Stage 5
            Case $CDDS_ITEMPOSTPAINT             ; After an item has been painted
              ConsoleWrite( "WindowProc Stage 5: CDDS_ITEMPOSTPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NEWFONT               ; $CDRF_NEWFONT must be returned after changing font or colors

            ; Stage 6
            Case $CDDS_POSTPAINT                 ; After the paint cycle is complete
              ConsoleWrite( "WindowProc Stage 6: CDDS_POSTPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NEWFONT               ; $CDRF_NEWFONT must be returned after changing font or colors
          EndSwitch
        Case $NM_CLICK
          ConsoleWrite( "WindowProc NM_CLICK " & "(" & $nMsg & ")" & @CRLF )
      EndSwitch
  EndSwitch
  ; Call next function in subclass chain (this forwards messages to main GUI)
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $iSubclassId
EndFunc

; WM_NOTIFY message handler
Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
  Local $hWndFrom = HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
  Local $iCode = DllStructGetData( $tNMHDR, "Code" )

  If Not $bSubclass Then
    $nMsg += 1
  Else
    $bSubclass = False
  EndIf

  Switch $hWndFrom
    Case $hListView1
      ConsoleWrite( "WM_NOTIFY  function: ListView 1 " & "(" & $nMsg & ")" & @CRLF )
    Case $hListView2
      ConsoleWrite( "WM_NOTIFY  function: ListView 2 " & "(" & $nMsg & ")" & @CRLF )
    Case $hListView3
      ConsoleWrite( "WM_NOTIFY  function: ListView 3 " & "(" & $nMsg & ")" & @CRLF )
    Case $hListView4
      ConsoleWrite( "WM_NOTIFY  function: ListView 4 " & "(" & $nMsg & ")" & @CRLF )
  EndSwitch

  Switch $hWndFrom
    Case $hListView1, $hListView2, $hListView3, $hListView4
      Switch $iCode
        Case $NM_CUSTOMDRAW
          ConsoleWrite( "WM_NOTIFY  NM_CUSTOMDRAW " & "(" & $nMsg & ")" & @CRLF )
        Case $NM_CLICK
          ConsoleWrite( "WM_NOTIFY  NM_CLICK " & "(" & $nMsg & ")" & @CRLF )
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam
EndFunc

This is a group of listviews, which uses a single callback function (that was a hint for you Mr. JohnOne).

The code for subclassing the listviews (more presicely the parent of the listviews which is the GUI) looks like this:

; Subclass GUI (ListView 1)
_WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 1, $hListView1 ) ; SubclassId = 1, $pData = $hListView1

; Subclass GUI (ListView 2)
_WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 2, $hListView2 ) ; SubclassId = 2, $pData = $hListView2

; Subclass GUI (ListView 3)
_WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 3, $hListView3 ) ; SubclassId = 3, $pData = $hListView3

; Subclass GUI (ListView 4)
_WinAPI_SetWindowSubclass( $hGui, $pWindowProc, 4, $hListView4 ) ; SubclassId = 4, $pData = $hListView4

Because the pointer ($pWindowProc) for the callback function (WindowProc) is the same for all callbacks, the SubclassId must be different for all callbacks to be able to create unique callbacks.

Note that the SubclassId is also a parameter in the callback function. You can use this parameter to execute different code sections for different listviews. I'm using the $pData parameter to identify the listviews. $pData is also a parameter in the callback function.

This is the flow chart:

Subclassing-36_zps3ktinja0.png

Each WM_MESSAGE is going through 4 callback functions (since it's the same callback function it's probably more correct to say that the callback function is called 4 times for each WM_MESSAGE) before it reaches the internal AutoIt message handler.

You can verify this by watching the output in SciTE console. Try to move the mouse around in the GUI across the title bar, the empty space and the listviews. And watch the SciTE output. You should also try to turn subclassing on/off for one, or more or all listviews.

It's obvious that if we continue to add listviews in that way, then we'll sooner or later run into a serious performance issue. What can we do about that?

Subclassing1.7z

Edited by LarsJ
Link to comment
Share on other sites

We can create each listview in its own child window, and then we can subclass the child windows. This will effectively separate the message flow from one listview from the message flow from all the other listviews and from the message flow from the GUI.

Code. Also in the zip below.

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIShellEx.au3>
#include "GuiListViewEx.au3"

Opt( "MustDeclareVars", 1 )
Opt( "GUIResizeMode", $GUI_DOCKALL )

Global $hListView1, $hListView2, $hListView3, $hListView4, $bSubclass, $bAllMsgs = True, $nMsg = 0

Example()


Func Example()
  ; Create GUI
  Local $hGui = GUICreate( "Custom draw, Childs", 830, 420 )

  ; ------------------------ ListView 1 ------------------------

  GUICtrlCreateLabel( "Child window 1 / ListView 1", 270, 25, 130, 15 )
  Local $idBut1 = GUICtrlCreateButton( "Turn subclassing off", 10, 10, 150, 25 ), $bEnabled1 = True
  GUICtrlSetBkColor( $idBut1, 0xCCFFCC )

  ; Create child window
  Local $hChild1 = GUICreate( "", 400, 180, 10, 40, $WS_CHILD, -1, $hGui )
  GUISetState( @SW_SHOW, $hChild1 )

  ; Create ListView
  Local $idListView1 = GUICtrlCreateListView( "", 0, 0, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView1 = GUICtrlGetHandle( $idListView1 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView1, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView1, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView1, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView1, "Column 4", 94 )

  ; Fill ListView
  Local $iItems1 = 3
  For $i = 0 To $iItems1 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView1 )
  Next

  ; Adjust height of GUI, child window and ListView to fit ten rows
  Local $iLvHeight = _GUICtrlListView_GetHeightToFitRows( $hListView1, 10 )
  WinMove( $hGui, "", Default, Default, Default, WinGetPos( $hGui )[3] - WinGetClientSize( $hGui )[1] + 2*$iLvHeight + 30 + 60 + 35 )
  WinMove( $hChild1, "", Default, Default, Default, $iLvHeight )
  WinMove( $hListView1, "", 0, 0, Default, $iLvHeight )

  ; ------------------------ ListView 2 ------------------------

  GUISwitch( $hGui )
  GUICtrlCreateLabel( "Child window 2 / ListView 2", 680, 25, 130, 15 )
  Local $idBut2 = GUICtrlCreateButton( "Turn subclassing off", 420, 10, 150, 25 ), $bEnabled2 = True
  GUICtrlSetBkColor( $idBut2, 0xCCFFCC )

  ; Create child window
  Local $hChild2 = GUICreate( "", 400, 180, 420, 40, $WS_CHILD, -1, $hGui )
  GUISetState( @SW_SHOW, $hChild2 )

  ; Create ListView
  Local $idListView2 = GUICtrlCreateListView( "", 0, 0, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView2 = GUICtrlGetHandle( $idListView2 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView2, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView2, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView2, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView2, "Column 4", 94 )

  ; Fill ListView
  Local $iItems2 = 3
  For $i = 0 To $iItems2 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView2 )
  Next

  ; Adjust height of child window and ListView to fit ten rows
  WinMove( $hChild2, "", Default, Default, Default, $iLvHeight )
  WinMove( $hListView2, "", 0, 0, Default, $iLvHeight )

  ; ------------------------ ListView 3 ------------------------

  GUISwitch( $hGui )
  GUICtrlCreateLabel( "Child window 3 / ListView 3", 270, 20+60+$iLvHeight-15, 130, 15 )
  Local $idBut3 = GUICtrlCreateButton( "Turn subclassing off", 10, 20+30+$iLvHeight, 150, 25 ), $bEnabled3 = True
  GUICtrlSetBkColor( $idBut3, 0xCCFFCC )

  ; Create child window
  Local $hChild3 = GUICreate( "", 400, 180, 10, 20+60+$iLvHeight, $WS_CHILD, -1, $hGui )
  GUISetState( @SW_SHOW, $hChild3 )

  ; Create ListView
  Local $idListView3 = GUICtrlCreateListView( "", 0, 0, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView3 = GUICtrlGetHandle( $idListView3 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView3, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView3, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView3, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView3, "Column 4", 94 )

  ; Fill ListView
  Local $iItems3 = 3
  For $i = 0 To $iItems3 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView3 )
  Next

  ; Adjust height of child window and ListView to fit ten rows
  WinMove( $hChild3, "", Default, Default, Default, $iLvHeight )
  WinMove( $hListView3, "", 0, 0, Default, $iLvHeight )

  ; ------------------------ ListView 4 ------------------------

  GUISwitch( $hGui )
  GUICtrlCreateLabel( "Child window 4 / ListView 4", 680, 20+60+$iLvHeight-15, 130, 15 )
  Local $idBut4 = GUICtrlCreateButton( "Turn subclassing off", 420, 20+30+$iLvHeight, 150, 25 ), $bEnabled4 = True
  GUICtrlSetBkColor( $idBut4, 0xCCFFCC )

  ; Create child window
  Local $hChild4 = GUICreate( "", 400, 180, 420, 20+60+$iLvHeight, $WS_CHILD, -1, $hGui )
  GUISetState( @SW_SHOW, $hChild4 )

  ; Create ListView
  Local $idListView4 = GUICtrlCreateListView( "", 0, 0, 400, 180, $GUI_SS_DEFAULT_LISTVIEW, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT )
  $hListView4 = GUICtrlGetHandle( $idListView4 )

  ; Add columns to ListView
  _GUICtrlListView_AddColumn( $idListView4, "Column 1", 94 )
  _GUICtrlListView_AddColumn( $idListView4, "Column 2", 94 )
  _GUICtrlListView_AddColumn( $idListView4, "Column 3", 94 )
  _GUICtrlListView_AddColumn( $idListView4, "Column 4", 94 )

  ; Fill ListView
  Local $iItems4 = 3
  For $i = 0 To $iItems4 - 1
    GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView4 )
  Next

  ; Adjust height of child window and ListView to fit ten rows
  WinMove( $hChild4, "", Default, Default, Default, $iLvHeight )
  WinMove( $hListView4, "", 0, 0, Default, $iLvHeight )

  ; ------------------------------------------------------------

  ; Create some controls
  GUISwitch( $hGui )
                    GUICtrlCreateButton( "Button",               10, 20+60+2*$iLvHeight+10,   150, 25 )
                    GUICtrlCreateLabel ( "ChildProc function:", 450, 20+60+2*$iLvHeight+10+5, 130, 15 )
  Local $idRadio1 = GUICtrlCreateRadio ( "All messages",        580, 20+60+2*$iLvHeight+10,    90, 25 ), $bRadio1Checked = True
  Local $idRadio2 = GUICtrlCreateRadio ( "WM_NOTIFY messages",  680, 20+60+2*$iLvHeight+10,   140, 25 )
  GUICtrlSetState( $idRadio1, $GUI_CHECKED )

  ; Register callback function to subclass child windows
  Local $pChildProc = DllCallbackGetPtr( DllCallbackRegister( "ChildProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )

  ; Subclass child window 1
  _WinAPI_SetWindowSubclass( $hChild1, $pChildProc, 1, $hListView1 ) ; SubclassId = 1, $pData = $hListView1

  ; Subclass child window 2
  _WinAPI_SetWindowSubclass( $hChild2, $pChildProc, 2, $hListView2 ) ; SubclassId = 2, $pData = $hListView2

  ; Subclass child window 3
  _WinAPI_SetWindowSubclass( $hChild3, $pChildProc, 3, $hListView3 ) ; SubclassId = 3, $pData = $hListView3

  ; Subclass child window 4
  _WinAPI_SetWindowSubclass( $hChild4, $pChildProc, 4, $hListView4 ) ; SubclassId = 4, $pData = $hListView4

  ; Register WM_NOTIFY message handler
  GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )

  ; Show GUI
  GUISetState( @SW_SHOW )

  ; Message loop
  While 1
    Local $aMsg = GUIGetMsg(1)
    Switch $aMsg[1]
      Case $hGui
        Switch $aMsg[0]
          Case $idBut1
            If $bEnabled1 Then
              GUICtrlSetData( $idBut1, "Turn subclassing on" )
              _WinAPI_RemoveWindowSubclass( $hChild1, $pChildProc, 1 )
              GUICtrlSetBkColor( $idBut1, 0xFFCCCC )
            Else
              GUICtrlSetData( $idBut1, "Turn subclassing off" )
              _WinAPI_SetWindowSubclass( $hChild1, $pChildProc, 1, $hListView1 ) ; SubclassId = 1, $pData = $hListView1
              GUICtrlSetBkColor( $idBut1, 0xCCFFCC )
            EndIf
            $bEnabled1 = Not $bEnabled1

          Case $idBut2
            If $bEnabled2 Then
              GUICtrlSetData( $idBut2, "Turn subclassing on" )
              _WinAPI_RemoveWindowSubclass( $hChild2, $pChildProc, 2 )
              GUICtrlSetBkColor( $idBut2, 0xFFCCCC )
            Else
              GUICtrlSetData( $idBut2, "Turn subclassing off" )
              _WinAPI_SetWindowSubclass( $hChild2, $pChildProc, 2, $hListView2 ) ; SubclassId = 2, $pData = $hListView2
              GUICtrlSetBkColor( $idBut2, 0xCCFFCC )
            EndIf
            $bEnabled2 = Not $bEnabled2

          Case $idBut3
            If $bEnabled3 Then
              GUICtrlSetData( $idBut3, "Turn subclassing on" )
              _WinAPI_RemoveWindowSubclass( $hChild3, $pChildProc, 3 )
              GUICtrlSetBkColor( $idBut3, 0xFFCCCC )
            Else
              GUICtrlSetData( $idBut3, "Turn subclassing off" )
              _WinAPI_SetWindowSubclass( $hChild3, $pChildProc, 3, $hListView3 ) ; SubclassId = 3, $pData = $hListView3
              GUICtrlSetBkColor( $idBut3, 0xCCFFCC )
            EndIf
            $bEnabled3 = Not $bEnabled3

          Case $idBut4
            If $bEnabled4 Then
              GUICtrlSetData( $idBut4, "Turn subclassing on" )
              _WinAPI_RemoveWindowSubclass( $hChild4, $pChildProc, 4 )
              GUICtrlSetBkColor( $idBut4, 0xFFCCCC )
            Else
              GUICtrlSetData( $idBut4, "Turn subclassing off" )
              _WinAPI_SetWindowSubclass( $hChild4, $pChildProc, 4, $hListView4 ) ; SubclassId = 4, $pData = $hListView4
              GUICtrlSetBkColor( $idBut4, 0xCCFFCC )
            EndIf
            $bEnabled4 = Not $bEnabled4

          Case $idRadio1, $idRadio2
            GUICtrlSetState( $idRadio1, $bRadio1Checked ? $GUI_UNCHECKED : $GUI_CHECKED )
            GUICtrlSetState( $idRadio2, $bRadio1Checked ? $GUI_CHECKED : $GUI_UNCHECKED )
            $bRadio1Checked = Not $bRadio1Checked
            $bAllMsgs = $bRadio1Checked

          Case $GUI_EVENT_CLOSE
            ExitLoop
        EndSwitch
    EndSwitch
  WEnd

  ; Cleanup
  _WinAPI_RemoveWindowSubclass( $hChild1, $pChildProc, 1 )
  _WinAPI_RemoveWindowSubclass( $hChild2, $pChildProc, 2 )
  _WinAPI_RemoveWindowSubclass( $hChild3, $pChildProc, 3 )
  _WinAPI_RemoveWindowSubclass( $hChild4, $pChildProc, 4 )
  GUIDelete()
EndFunc

; ChildProc callback function
Func ChildProc( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $hListView )
  $bSubclass = True

  If $bAllMsgs Then
    $nMsg += 1
    Switch $hListView
      Case $hListView1
        ConsoleWrite( "ChildProc function: ListView 1 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView2
        ConsoleWrite( "ChildProc function: ListView 2 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView3
        ConsoleWrite( "ChildProc function: ListView 3 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView4
        ConsoleWrite( "ChildProc function: ListView 4 " & "(" & $nMsg & ")" & @CRLF )
    EndSwitch
  EndIf

  If $iMsg <> $WM_NOTIFY Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  If Not $bAllMsgs Then
    $nMsg += 1
    Switch $hListView
      Case $hListView1
        ConsoleWrite( "ChildProc function: ListView 1 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView2
        ConsoleWrite( "ChildProc function: ListView 2 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView3
        ConsoleWrite( "ChildProc function: ListView 3 " & "(" & $nMsg & ")" & @CRLF )
      Case $hListView4
        ConsoleWrite( "ChildProc function: ListView 4 " & "(" & $nMsg & ")" & @CRLF )
    EndSwitch
  EndIf

  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
  Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) ) ; $hWndFrom
    Case $hListView
      Switch DllStructGetData( $tNMHDR, "Code" ) ; $iCode
        Case $NM_CUSTOMDRAW
          Local $tNMLVCustomDraw = DllStructCreate( $tagNMLVCUSTOMDRAW, $lParam )
          Local $dwDrawStage = DllStructGetData( $tNMLVCustomDraw, "dwDrawStage" )
          Switch $dwDrawStage                    ; Specifies the drawing stage
            ; Stage 1
            Case $CDDS_PREPAINT                  ; Before the paint cycle begins
              ConsoleWrite( "ChildProc Stage 1: CDDS_PREPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NOTIFYITEMDRAW + _    ; Stage 2 will be carried out
                     $CDRF_NOTIFYPOSTPAINT       ; Stage 6 will be carried out
              Return $CDRF_NOTIFYITEMDRAW        ; Notify the parent window before an item is painted
              Return $CDRF_NOTIFYPOSTPAINT       ; Notify the parent window after the paint cycle is complete

            ; Stage 2
            Case $CDDS_ITEMPREPAINT              ; Before an item is painted
              ConsoleWrite( "ChildProc Stage 2: CDDS_ITEMPREPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NOTIFYSUBITEMDRAW + _ ; Stage 3 will be carried out
                     $CDRF_NOTIFYPOSTPAINT       ; Stage 5 will be carried out
              Return $CDRF_NOTIFYSUBITEMDRAW     ; Notify the parent window before a subitem is painted
              Return $CDRF_NOTIFYPOSTPAINT       ; Notify the parent window after an item is painted

            ; Stage 3
            Case BitOR( $CDDS_ITEMPREPAINT, _
                        $CDDS_SUBITEM )          ; Before a subitem is painted
              ConsoleWrite( "ChildProc Stage 3: CDDS_ITEMPREPAINT,  CDDS_SUBITEM " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NOTIFYPOSTPAINT       ; Stage 4 will be carried out
              Return $CDRF_NOTIFYPOSTPAINT       ; Notify the parent window after a subitem is painted

            ; Stage 4
            Case BitOR( $CDDS_ITEMPOSTPAINT, _
                        $CDDS_SUBITEM )          ; After a subitem has been painted
              ConsoleWrite( "ChildProc Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NEWFONT               ; $CDRF_NEWFONT must be returned after changing font or colors

            ; Stage 5
            Case $CDDS_ITEMPOSTPAINT             ; After an item has been painted
              ConsoleWrite( "ChildProc Stage 5: CDDS_ITEMPOSTPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NEWFONT               ; $CDRF_NEWFONT must be returned after changing font or colors

            ; Stage 6
            Case $CDDS_POSTPAINT                 ; After the paint cycle is complete
              ConsoleWrite( "ChildProc Stage 6: CDDS_POSTPAINT " & "(" & $nMsg & ")" & @CRLF )
              Return $CDRF_NEWFONT               ; $CDRF_NEWFONT must be returned after changing font or colors
          EndSwitch
        Case $NM_CLICK
          ConsoleWrite( "ChildProc NM_CLICK " & "(" & $nMsg & ")" & @CRLF )
      EndSwitch
  EndSwitch
  ; Call next function in subclass chain (this forwards messages to main GUI)
  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $iSubclassId
EndFunc

; WM_NOTIFY message handler
Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
  Local $hWndFrom = HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
  Local $iCode = DllStructGetData( $tNMHDR, "Code" )

  If Not $bSubclass Then
    $nMsg += 1
  Else
    $bSubclass = False
  EndIf

  Switch $hWndFrom
    Case $hListView1
      ConsoleWrite( "WM_NOTIFY function: ListView 1 " & "(" & $nMsg & ")" & @CRLF )
    Case $hListView2
      ConsoleWrite( "WM_NOTIFY function: ListView 2 " & "(" & $nMsg & ")" & @CRLF )
    Case $hListView3
      ConsoleWrite( "WM_NOTIFY function: ListView 3 " & "(" & $nMsg & ")" & @CRLF )
    Case $hListView4
      ConsoleWrite( "WM_NOTIFY function: ListView 4 " & "(" & $nMsg & ")" & @CRLF )
  EndSwitch

  Switch $hWndFrom
    Case $hListView1, $hListView2, $hListView3, $hListView4
      Switch $iCode
        Case $NM_CUSTOMDRAW
          ConsoleWrite( "WM_NOTIFY NM_CUSTOMDRAW " & "(" & $nMsg & ")" & @CRLF )
        Case $NM_CLICK
          ConsoleWrite( "WM_NOTIFY NM_CLICK " & "(" & $nMsg & ")" & @CRLF )
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam
EndFunc

subclassing code:

; Subclass child window 1
_WinAPI_SetWindowSubclass( $hChild1, $pChildProc, 1, $hListView1 ) ; SubclassId = 1, $pData = $hListView1

; Subclass child window 2
_WinAPI_SetWindowSubclass( $hChild2, $pChildProc, 2, $hListView2 ) ; SubclassId = 2, $pData = $hListView2

; Subclass child window 3
_WinAPI_SetWindowSubclass( $hChild3, $pChildProc, 3, $hListView3 ) ; SubclassId = 3, $pData = $hListView3

; Subclass child window 4
_WinAPI_SetWindowSubclass( $hChild4, $pChildProc, 4, $hListView4 ) ; SubclassId = 4, $pData = $hListView4

Flow chart:

Subclassing-37_zps45gvikgh.png

Watch the output in SciTE console in the same way as before.

You can continue adding listviews in that way until you run out of control IDs (or another resource).

Hopefully you have not forgotten the whole point of all this: You can implement the code including the message handler in a UDF.

One last issue which is also related to performance. If you implement a subclass based message handler in a UDF to respond to custom draw notifications or similar, then make sure to return one of the valid custom draw return values in all of the different custom draw stages. In this way you can avoid that the custom draw notifications are forwarded to a user defined message handler created with GUIRegisterMsg. Because there are so many notifications and you have already spent time executing your own code, it can easily be a performance issue if there is also executed code in a user defined message handler. Even if it's just a few lines because there is no matching case statement. You can see how it's done in the example.

Subclassing2.7z

Edited by LarsJ
Link to comment
Share on other sites

@LarsJ Thanks a lot for this tutorial. I wrote something else in this post, but deleted due to bad grammar. This is the main problem i am facing. I cannot express my thanks and gratitude through words. Whenever i complete writing i found so many errors. So i deleted a lot of words which says my gratitude. I will sure get some free time and read your replys. mikell is right. This post is helpful for many peoples. :) 

Spoiler

My Contributions

Glance GUI Library - A gui library based on Windows api functions. Written in Nim programming language.

UDF Link Viewer   --- A tool to visit the links of some most important UDFs 

 Includer_2  ----- A tool to type the #include statement automatically 

 Digits To Date  ----- date from 3 integer values

PrintList ----- prints arrays into console for testing.

 Alert  ------ An alternative for MsgBox 

 MousePosition ------- A simple tooltip display of mouse position

GRM Helper -------- A littile tool to help writing code with GUIRegisterMsg function

Access_UDF  -------- An UDF for working with access database files. (.*accdb only)

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...