kcvinu Posted February 26, 2016 Author Posted February 26, 2016 (edited) @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 February 26, 2016 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)
LarsJ Posted February 26, 2016 Posted February 26, 2016 I'll come back with an answer to your questions. In the meantime, you can look at this example. It's much more straightforward. kcvinu 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
kcvinu Posted February 26, 2016 Author Posted February 26, 2016 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)
LarsJ Posted February 26, 2016 Posted February 26, 2016 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. Gianni and kcvinu 2 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
JohnOne Posted February 26, 2016 Posted February 26, 2016 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? LarsJ 1 AutoIt Absolute Beginners Require a serial Pause Script Video Tutorials by Morthawt ipify Monkey's are, like, natures humans.
LarsJ Posted February 26, 2016 Posted February 26, 2016 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. JohnOne and Gianni 2 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
LarsJ Posted February 27, 2016 Posted February 27, 2016 (edited) 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: expandcollapse popup#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: expandcollapse popup#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: Edited February 27, 2016 by LarsJ JohnOne, kcvinu and Gianni 3 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
LarsJ Posted February 27, 2016 Posted February 27, 2016 Now we can try to implement both methods in the same script: expandcollapse popup#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: expandcollapse popup#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. JohnOne, kcvinu and Gianni 3 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
LarsJ Posted February 27, 2016 Posted February 27, 2016 Finally we can implement the subclassing functions in a small UDF: Save as Subclassing.au3: expandcollapse popup#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 expandcollapse popup#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 kcvinu, Gianni and JohnOne 3 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
kcvinu Posted February 27, 2016 Author Posted February 27, 2016 @LarsJ Wow !. This seems to be a complete tutorial on that subject. Thank you. Let me read this. 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)
LarsJ Posted February 27, 2016 Posted February 27, 2016 You are welcome, you master of subclassing. Don't miss the question above the picture in post 27. Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
kcvinu Posted February 27, 2016 Author Posted February 27, 2016 @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)
kcvinu Posted February 27, 2016 Author Posted February 27, 2016 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)
LarsJ Posted February 27, 2016 Posted February 27, 2016 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. kcvinu 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
kcvinu Posted February 28, 2016 Author Posted February 28, 2016 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)
LarsJ Posted February 28, 2016 Posted February 28, 2016 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 subclassingSubclassing 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. 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. JohnOne and mikell 2 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
LarsJ Posted February 28, 2016 Posted February 28, 2016 (edited) 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. expandcollapse popup#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: 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 February 28, 2016 by LarsJ JohnOne, Danyfirex and mikell 3 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
LarsJ Posted February 28, 2016 Posted February 28, 2016 (edited) 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. expandcollapse popup#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: 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 February 28, 2016 by LarsJ mikell, JohnOne, Danyfirex and 1 other 4 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions
mikell Posted February 28, 2016 Posted February 28, 2016 LarsJ, An absolutely great tutorial, nice and clear Many thanks for this very helpful lesson
kcvinu Posted February 28, 2016 Author Posted February 28, 2016 @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)
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now