kurtykurtyboy Posted February 16, 2017 Share Posted February 16, 2017 (edited) Hello everyone. I'm very familiar with AutoIt, but not subclassing so I am a bit in over my head. Apologies for the lengthy post, but I want to give as much detail as I can. The problem I'm trying to make a combobox that has a custom look. My understanding is I need to do the following: Create combobox with $CBS_OWNERDRAWVARIABLE style Subclass the GUI for WM_MEASUREITEM & WM_DRAWITEM messages Subclass the combobox for WM_PAINT messages I've managed to piece together examples from other languages and topics on this forum. Using GUIRegisterMsg for WM_MEASUREITEM & WM_DRAWITEM seems to work well along with my combobox subclass function. Much of my understanding comes from this post. how to use all messages in guigetmsg HOWEVER! I would like to make it into a UDF, so instead of GUIRegisteMsg, I would use _WinAPI_SetWindowSubclass and process the MEASUREITEM & DRAWITEM messages in the subclass function. I've set it up how I think it should work, but I am having an issue with drawitem. When I move the cursor up and down through the items, the items get randomly painted as seen in the image below. The problem only occurs when using both the window subclass AND combobox subclass: - If I comment out my combobox subclass, comment out the window subclass and use GUIRegisterMsg for DRAWITEM, the drawitem looks fine. - If I comment out my combobox subclass, comment out GUIRegisterMsg, and uncomment the window subclass, the drawitem looks fine. - If I uncomment the combobox subclass, comment out the window subclass, and use GUIRegisterMsg, the drawitem looks fine. Why does it work properly with GUIRegisterMsg but not with _WinAPI_SetWindowSubclass? This happens even when the combobox subclass function is not doing anything. expandcollapse popup#include <GUIComboBox.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPIShellEx.au3> #include <WinAPIGdi.au3> Global Const $clrWindowText = 0xEEEEEE Global Const $clrHighlightText = 0xFFFFFF Global Const $clrHighlight = 0x777777 Global Const $clrWindow = 0x444444 Global $hBrushNorm = _WinAPI_CreateSolidBrush($clrWindow) Global $hBrushSel = _WinAPI_CreateSolidBrush($clrHighlight) Global $hPen = _WinAPI_CreatePen($PS_SOLID, 2, $clrHighlight) Global $hGUI, $ComboBox, $hComboBox Global $windowcallback, $pwindowcallback, $combocallback, $pcombocallback Example() Func Example() OnAutoItExitRegister("_customcombobox_cleanup") GUIRegisterMsg($WM_MEASUREITEM, '_WM_MEASUREITEM') ;~ GUIRegisterMsg($WM_DRAWITEM, '_WM_DRAWITEM') $hGUI = GUICreate('Test', 220, 300) $ComboBox = GUICtrlCreateCombo('', 10, 10, 200, 300, BitOR($WS_CHILD, $CBS_OWNERDRAWVARIABLE, $CBS_HASSTRINGS, $CBS_DROPDOWNLIST)) GUICtrlSetData($ComboBox, "Apple|Banana|Orange|Pear|Plum|WatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelon") $hComboBox = GUICtrlGetHandle($ComboBox) ;subclass window for WM_DRAWITEM $windowcallback = DllCallbackRegister("_customCombobox_WindowCallback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pwindowcallback = DllCallbackGetPtr($windowcallback) _WinAPI_SetWindowSubclass($hGUI, $pwindowcallback, 1, $ComboBox) ; $iSubclassId = 1, $pData = CtrlID ;subclass combobox for WM_PAINT $combocallback = DllCallbackRegister("_customCombobox_Callback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pcombocallback = DllCallbackGetPtr($combocallback) _WinAPI_SetWindowSubclass(GUICtrlGetHandle($ComboBox), $pcombocallback, 2, $ComboBox) ; $iSubclassId = 2, $pData = CtrlID GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _onExit() EndSwitch WEnd EndFunc ;==>Example ;subclassed window function --> WM_DRAWITEM Func _customCombobox_WindowCallback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) If $iMsg <> $WM_DRAWITEM Then Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] Local Const $tagDRAWITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemAction;' & _ 'uint itemState;' & _ 'hwnd hwndItem;' & _ 'hwnd hDC;' & _ $tagRECT & _ ';ulong_ptr itemData;' Local Const $ODT_COMBOBOX = 3 Local Const $ODS_SELECTED = 1 Local Const $ODS_COMBOBOXEDIT = 4096 Switch $iMsg Case $WM_DRAWITEM Local $tDIS Local $iCtlType, $iCtlID, $iItemID, $iItemAction, $iItemState Local $clrForeground, $clrBackground Local $hWndItem, $hDC, $hOldPen, $hOldBrush Local $tRect, $aRect[4] Local $sText, $iCode, $iIDFrom $tDIS = DllStructCreate($tagDRAWITEMSTRUCT, $lParam) $iCtlType = DllStructGetData($tDIS, 'CtlType') $iCtlID = DllStructGetData($tDIS, 'CtlID') $iItemID = DllStructGetData($tDIS, 'itemID') $iItemAction = DllStructGetData($tDIS, 'itemAction') $iItemState = DllStructGetData($tDIS, 'itemState') $hWndItem = DllStructGetData($tDIS, 'hwndItem') $hDC = DllStructGetData($tDIS, 'hDC') $tRect = DllStructCreate($tagRECT) If $iCtlType = $ODT_COMBOBOX Then If $iCtlID == $pData Then ;if my combobox For $i = 1 To 4 DllStructSetData($tRect, $i, DllStructGetData($tDIS, $i + 7)) $aRect[$i - 1] = DllStructGetData($tRect, $i) Next _GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) If BitAND($iItemState, $ODS_SELECTED) And Not BitAND($iItemState, $ODS_COMBOBOXEDIT) Then $hOldBrush = _WinAPI_SelectObject($hDC, $hBrushSel) $hOldPen = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_Rectangle_Pts($hDC, $aRect[0] + 1, $aRect[1] + 1, $aRect[2], $aRect[3]) _WinAPI_SelectObject($hDC, $hOldPen) _WinAPI_SelectObject($hDC, $hOldBrush) $clrForeground = _WinAPI_SetTextColor($hDC, $clrHighlightText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrHighlight) Else $clrForeground = _WinAPI_SetTextColor($hDC, $clrWindowText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrWindow) _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrushNorm) EndIf DllStructSetData($tRect, "Left", $aRect[0] + 4) DllStructSetData($tRect, "Top", $aRect[1] + 4) DllStructSetData($tRect, "Bottom", $aRect[3] - 2) _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_VCENTER)) _WinAPI_SetTextColor($hDC, $clrForeground) _WinAPI_SetBkColor($hDC, $clrBackground) Return True EndIf EndIf EndSwitch ;default subclass proc Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] EndFunc ;==>_customCombobox_WindowCallback ;guiregistermsg WM_DRAWITEM Func _WM_DRAWITEM($hWnd, $iMsg, $iwParam, $ilParam) Local Const $tagDRAWITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemAction;' & _ 'uint itemState;' & _ 'hwnd hwndItem;' & _ 'hwnd hDC;' & _ $tagRECT & _ ';ulong_ptr itemData;' Local Const $ODT_COMBOBOX = 3 Local Const $ODS_SELECTED = 1 Local Const $ODS_COMBOBOXEDIT = 4096 Local $tDIS = DllStructCreate($tagDRAWITEMSTRUCT, $ilParam) Local $iCtlType, $iCtlID, $iItemID, $iItemAction, $iItemState Local $clrForeground, $clrBackground Local $hWndItem, $hDC, $hOldPen, $hOldBrush Local $tRect, $aRect[4] Local $sText $iCtlType = DllStructGetData($tDIS, 'CtlType') $iCtlID = DllStructGetData($tDIS, 'CtlID') $iItemID = DllStructGetData($tDIS, 'itemID') $iItemAction = DllStructGetData($tDIS, 'itemAction') $iItemState = DllStructGetData($tDIS, 'itemState') $hWndItem = DllStructGetData($tDIS, 'hwndItem') $hDC = DllStructGetData($tDIS, 'hDC') $tRect = DllStructCreate($tagRECT) If $iCtlType = $ODT_COMBOBOX Then Switch $iCtlID Case $ComboBox For $i = 1 To 4 DllStructSetData($tRect, $i, DllStructGetData($tDIS, $i + 7)) $aRect[$i - 1] = DllStructGetData($tRect, $i) Next _GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) If BitAND($iItemState, $ODS_SELECTED) And Not BitAND($iItemState, $ODS_COMBOBOXEDIT) Then $hOldBrush = _WinAPI_SelectObject($hDC, $hBrushSel) $hOldPen = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_Rectangle_Pts($hDC, $aRect[0] + 1, $aRect[1] + 1, $aRect[2], $aRect[3]) _WinAPI_SelectObject($hDC, $hOldPen) _WinAPI_SelectObject($hDC, $hOldBrush) $clrForeground = _WinAPI_SetTextColor($hDC, $clrHighlightText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrHighlight) Else $clrForeground = _WinAPI_SetTextColor($hDC, $clrWindowText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrWindow) _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrushNorm) EndIf DllStructSetData($tRect, "Left", $aRect[0] + 4) DllStructSetData($tRect, "Top", $aRect[1] + 4) DllStructSetData($tRect, "Bottom", $aRect[3] - 2) _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_VCENTER)) _WinAPI_SetTextColor($hDC, $clrForeground) _WinAPI_SetBkColor($hDC, $clrBackground) EndSwitch EndIf Return $GUI_RUNDEFMSG EndFunc ;==>_WM_DRAWITEM ;callback function for combobox Func _customCombobox_Callback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) ;do stuff here ;run default subclass proc Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] EndFunc ;==>_customCombobox_Callback ;guiregistermsg WM_MEASUREITEM Func _WM_MEASUREITEM($hWnd, $iMsg, $iwParam, $ilParam) Local Const $tagMEASUREITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemWidth;' & _ 'uint itemHeight;' & _ 'ulong_ptr itemData;' Local Const $ODT_COMBOBOX = 3 Local $tMIS = DllStructCreate($tagMEASUREITEMSTRUCT, $ilParam) Local $iCtlType, $iCtlID, $iItemID, $iItemWidth, $iItemHeight Local $hComboBox Local $tSize Local $sText $iCtlType = DllStructGetData($tMIS, 'CtlType') $iCtlID = DllStructGetData($tMIS, 'CtlID') $iItemID = DllStructGetData($tMIS, 'itemID') $iItemWidth = DllStructGetData($tMIS, 'itemWidth') $iItemHeight = DllStructGetData($tMIS, 'itemHeight') $hComboBox = GUICtrlGetHandle($iCtlID) If $iCtlType = $ODT_COMBOBOX Then DllStructSetData($tMIS, 'itemHeight', 20) EndIf Return $GUI_RUNDEFMSG EndFunc ;==>_WM_MEASUREITEM Func _WinAPI_Rectangle_Pts($hDC, $iLeft, $iTop, $iRight, $iBottom) Local $aResult = DllCall("gdi32.dll", "int", "Rectangle", "hwnd", $hDC, "int", $iLeft, "int", $iTop, "int", $iRight, "int", $iBottom) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] <> 0 EndFunc ;==>_WinAPI_Rectangle_Pts Func _onExit() _WinAPI_DeleteObject($hPen) _WinAPI_DeleteObject($hBrushSel) _WinAPI_DeleteObject($hBrushNorm) Exit EndFunc ;==>_onExit Func _customcombobox_cleanup() _WinAPI_RemoveWindowSubclass($hGUI, $pwindowcallback, 1) DllCallbackFree($windowcallback) _WinAPI_RemoveWindowSubclass($hGUI, $pcombocallback, 2) DllCallbackFree($combocallback) EndFunc ;==>_customcombobox_cleanup Edited February 16, 2017 by kurtykurtyboy more information Link to comment Share on other sites More sharing options...
LarsJ Posted February 17, 2017 Share Posted February 17, 2017 (edited) The code is almost OK. But you make it a little bit harder than it really is. To implement message handlers through subclassing you can pretty much just make a direct translation of the message handlers based on GUIRegisterMsg. If you don't have to handle WM_PAINT messages in the GUIRegisterMsg code, you don't have to handle WM_PAINT messages in the subclassing code. Delete the WM_PAINT code and everything will work. In AutoIt you usually never need to handle WM_PAINT messages in your own code. They are handled very well in the internal code. When you implement subclassing, you can create a subclass with _WinAPI_SetWindowSubclass for each control or each child window or the main GUI. But you do not create a subclass for each WM_MESSAGE. This will inevitably lead to performance issues. Here are two versions of your code that handles $WM_MEASUREITEM (height changed from 20 to 30 to show that it's working) and $WM_DRAWITEM messages. The first is based on message handlers created with GUIRegisterMsg. The second is based on a message handler implemented through subclassing. Note how similar the code is in the two versions. GUIRegisterMsg: expandcollapse popup#include <GUIComboBox.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPIShellEx.au3> #include <WinAPIGdi.au3> Global Const $clrWindowText = 0xEEEEEE Global Const $clrHighlightText = 0xFFFFFF Global Const $clrHighlight = 0x777777 Global Const $clrWindow = 0x444444 Global $hBrushNorm = _WinAPI_CreateSolidBrush($clrWindow) Global $hBrushSel = _WinAPI_CreateSolidBrush($clrHighlight) Global $hPen = _WinAPI_CreatePen($PS_SOLID, 2, $clrHighlight) Global $hGUI, $ComboBox, $hComboBox Global $windowcallback, $pwindowcallback, $combocallback, $pcombocallback OnAutoItExitRegister("_customcombobox_cleanup") ; OnAutoItExitRegister should be placed here, not in the function <<<<<<<<<<<<<<<<<<<<< Example() Func Example() GUIRegisterMsg($WM_MEASUREITEM, '_WM_MEASUREITEM') GUIRegisterMsg($WM_DRAWITEM, '_WM_DRAWITEM') $hGUI = GUICreate('Test', 220, 300) $ComboBox = GUICtrlCreateCombo('', 10, 10, 200, 300, BitOR($WS_CHILD, $CBS_OWNERDRAWVARIABLE, $CBS_HASSTRINGS, $CBS_DROPDOWNLIST)) GUICtrlSetData($ComboBox, "Apple|Banana|Orange|Pear|Plum|WatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelon") $hComboBox = GUICtrlGetHandle($ComboBox) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _onExit() EndSwitch WEnd EndFunc ;==>Example ;guiregistermsg WM_DRAWITEM Func _WM_DRAWITEM($hWnd, $iMsg, $iwParam, $ilParam) Local Const $tagDRAWITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemAction;' & _ 'uint itemState;' & _ 'hwnd hwndItem;' & _ 'hwnd hDC;' & _ $tagRECT & _ ';ulong_ptr itemData;' Local Const $ODT_COMBOBOX = 3 Local Const $ODS_SELECTED = 1 Local Const $ODS_COMBOBOXEDIT = 4096 Local $tDIS = DllStructCreate($tagDRAWITEMSTRUCT, $ilParam) Local $iCtlType, $iCtlID, $iItemID, $iItemAction, $iItemState Local $clrForeground, $clrBackground Local $hWndItem, $hDC, $hOldPen, $hOldBrush Local $tRect, $aRect[4] Local $sText $iCtlType = DllStructGetData($tDIS, 'CtlType') $iCtlID = DllStructGetData($tDIS, 'CtlID') $iItemID = DllStructGetData($tDIS, 'itemID') $iItemAction = DllStructGetData($tDIS, 'itemAction') $iItemState = DllStructGetData($tDIS, 'itemState') $hWndItem = DllStructGetData($tDIS, 'hwndItem') $hDC = DllStructGetData($tDIS, 'hDC') $tRect = DllStructCreate($tagRECT) If $iCtlType = $ODT_COMBOBOX Then Switch $iCtlID Case $ComboBox For $i = 1 To 4 DllStructSetData($tRect, $i, DllStructGetData($tDIS, $i + 7)) $aRect[$i - 1] = DllStructGetData($tRect, $i) Next _GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) If BitAND($iItemState, $ODS_SELECTED) And Not BitAND($iItemState, $ODS_COMBOBOXEDIT) Then $hOldBrush = _WinAPI_SelectObject($hDC, $hBrushSel) $hOldPen = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_Rectangle_Pts($hDC, $aRect[0] + 1, $aRect[1] + 1, $aRect[2], $aRect[3]) _WinAPI_SelectObject($hDC, $hOldPen) _WinAPI_SelectObject($hDC, $hOldBrush) $clrForeground = _WinAPI_SetTextColor($hDC, $clrHighlightText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrHighlight) Else $clrForeground = _WinAPI_SetTextColor($hDC, $clrWindowText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrWindow) _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrushNorm) EndIf DllStructSetData($tRect, "Left", $aRect[0] + 4) DllStructSetData($tRect, "Top", $aRect[1] + 4) DllStructSetData($tRect, "Bottom", $aRect[3] - 2) _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_VCENTER)) _WinAPI_SetTextColor($hDC, $clrForeground) _WinAPI_SetBkColor($hDC, $clrBackground) EndSwitch EndIf Return $GUI_RUNDEFMSG EndFunc ;==>_WM_DRAWITEM ;guiregistermsg WM_MEASUREITEM Func _WM_MEASUREITEM($hWnd, $iMsg, $iwParam, $ilParam) Local Const $tagMEASUREITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemWidth;' & _ 'uint itemHeight;' & _ 'ulong_ptr itemData;' Local Const $ODT_COMBOBOX = 3 Local $tMIS = DllStructCreate($tagMEASUREITEMSTRUCT, $ilParam) Local $iCtlType, $iCtlID, $iItemID, $iItemWidth, $iItemHeight Local $hComboBox Local $tSize Local $sText $iCtlType = DllStructGetData($tMIS, 'CtlType') $iCtlID = DllStructGetData($tMIS, 'CtlID') $iItemID = DllStructGetData($tMIS, 'itemID') $iItemWidth = DllStructGetData($tMIS, 'itemWidth') $iItemHeight = DllStructGetData($tMIS, 'itemHeight') $hComboBox = GUICtrlGetHandle($iCtlID) If $iCtlType = $ODT_COMBOBOX Then DllStructSetData($tMIS, 'itemHeight', 30) ; 20 -> 30 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< EndIf Return $GUI_RUNDEFMSG EndFunc ;==>_WM_MEASUREITEM Func _WinAPI_Rectangle_Pts($hDC, $iLeft, $iTop, $iRight, $iBottom) Local $aResult = DllCall("gdi32.dll", "int", "Rectangle", "hwnd", $hDC, "int", $iLeft, "int", $iTop, "int", $iRight, "int", $iBottom) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] <> 0 EndFunc ;==>_WinAPI_Rectangle_Pts Func _onExit() _WinAPI_DeleteObject($hPen) _WinAPI_DeleteObject($hBrushSel) _WinAPI_DeleteObject($hBrushNorm) Exit EndFunc ;==>_onExit Func _customcombobox_cleanup() GUIRegisterMsg($WM_MEASUREITEM, '') GUIRegisterMsg($WM_DRAWITEM, '') EndFunc ;==>_customcombobox_cleanup _WinAPI_SetWindowSubclass: expandcollapse popup#include <GUIComboBox.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPIShellEx.au3> #include <WinAPIGdi.au3> Global Const $clrWindowText = 0xEEEEEE Global Const $clrHighlightText = 0xFFFFFF Global Const $clrHighlight = 0x777777 Global Const $clrWindow = 0x444444 Global $hBrushNorm = _WinAPI_CreateSolidBrush($clrWindow) Global $hBrushSel = _WinAPI_CreateSolidBrush($clrHighlight) Global $hPen = _WinAPI_CreatePen($PS_SOLID, 2, $clrHighlight) Global $hGUI, $ComboBox, $hComboBox Global $windowcallback, $pwindowcallback, $combocallback, $pcombocallback OnAutoItExitRegister("_customcombobox_cleanup") ; OnAutoItExitRegister should be placed here, not in the function <<<<<<<<<<<<<<<<<<<<< Example() Func Example() $hGUI = GUICreate('Test', 220, 300) ;subclass the GUI ; The subclass must be implemented here to be able to respond to $WM_MEASUREITEM messages when the Combobox is created $windowcallback = DllCallbackRegister("_customCombobox_WindowCallback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pwindowcallback = DllCallbackGetPtr($windowcallback) _WinAPI_SetWindowSubclass($hGUI, $pwindowcallback, 1, 0) ; $iSubclassId = 1, $pData = 0 ($ComboBox is not available) $ComboBox = GUICtrlCreateCombo('', 10, 10, 200, 300, BitOR($WS_CHILD, $CBS_OWNERDRAWVARIABLE, $CBS_HASSTRINGS, $CBS_DROPDOWNLIST)) GUICtrlSetData($ComboBox, "Apple|Banana|Orange|Pear|Plum|WatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelon") $hComboBox = GUICtrlGetHandle($ComboBox) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _onExit() EndSwitch WEnd EndFunc ;==>Example ;subclassed window function --> GUI Func _customCombobox_WindowCallback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) If $iMsg <> $WM_DRAWITEM And $iMsg <> $WM_MEASUREITEM Then Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] Local Const $ODT_COMBOBOX = 3 Local Const $ODT_STATIC = 5 Local Const $ODS_SELECTED = 1 Local Const $ODS_COMBOBOXEDIT = 4096 Switch $iMsg Case $WM_DRAWITEM Local Const $tagDRAWITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemAction;' & _ 'uint itemState;' & _ 'hwnd hwndItem;' & _ 'hwnd hDC;' & _ $tagRECT & _ ';ulong_ptr itemData;' Local $tDIS Local $iCtlType, $iCtlID, $iItemID, $iItemAction, $iItemState Local $clrForeground, $clrBackground Local $hWndItem, $hDC, $hOldPen, $hOldBrush Local $tRect, $aRect[4] Local $sText, $iCode, $iIDFrom $tDIS = DllStructCreate($tagDRAWITEMSTRUCT, $lParam) $iCtlType = DllStructGetData($tDIS, 'CtlType') $iCtlID = DllStructGetData($tDIS, 'CtlID') $iItemID = DllStructGetData($tDIS, 'itemID') $iItemAction = DllStructGetData($tDIS, 'itemAction') $iItemState = DllStructGetData($tDIS, 'itemState') $hWndItem = DllStructGetData($tDIS, 'hwndItem') $hDC = DllStructGetData($tDIS, 'hDC') $tRect = DllStructCreate($tagRECT) If $iCtlType = $ODT_COMBOBOX Then If $iCtlID = $ComboBox Then ;if my combobox For $i = 1 To 4 DllStructSetData($tRect, $i, DllStructGetData($tDIS, $i + 7)) $aRect[$i - 1] = DllStructGetData($tRect, $i) Next _GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) If BitAND($iItemState, $ODS_SELECTED) And Not BitAND($iItemState, $ODS_COMBOBOXEDIT) Then $hOldBrush = _WinAPI_SelectObject($hDC, $hBrushSel) $hOldPen = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_Rectangle_Pts($hDC, $aRect[0] + 1, $aRect[1] + 1, $aRect[2], $aRect[3]) _WinAPI_SelectObject($hDC, $hOldPen) _WinAPI_SelectObject($hDC, $hOldBrush) $clrForeground = _WinAPI_SetTextColor($hDC, $clrHighlightText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrHighlight) Else $clrForeground = _WinAPI_SetTextColor($hDC, $clrWindowText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrWindow) _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrushNorm) EndIf DllStructSetData($tRect, "Left", $aRect[0] + 4) DllStructSetData($tRect, "Top", $aRect[1] + 4) DllStructSetData($tRect, "Bottom", $aRect[3] - 2) _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_VCENTER)) _WinAPI_SetTextColor($hDC, $clrForeground) _WinAPI_SetBkColor($hDC, $clrBackground) Return True EndIf EndIf Case $WM_MEASUREITEM Local Const $tagMEASUREITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemWidth;' & _ 'uint itemHeight;' & _ 'ulong_ptr itemData;' Local $tMIS = DllStructCreate($tagMEASUREITEMSTRUCT, $lparam) Local $iCtlType, $iCtlID, $iItemID, $iItemWidth, $iItemHeight Local $hComboBox Local $tSize Local $sText $iCtlType = DllStructGetData($tMIS, 'CtlType') $iCtlID = DllStructGetData($tMIS, 'CtlID') $iItemID = DllStructGetData($tMIS, 'itemID') $iItemWidth = DllStructGetData($tMIS, 'itemWidth') $iItemHeight = DllStructGetData($tMIS, 'itemHeight') $hComboBox = GUICtrlGetHandle($iCtlID) If $iCtlType = $ODT_COMBOBOX Then DllStructSetData($tMIS, 'itemHeight', 30) ; 20 -> 30 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< EndIf EndSwitch ;default subclass proc Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] EndFunc ;==>_customCombobox_WindowCallback Func _WinAPI_Rectangle_Pts($hDC, $iLeft, $iTop, $iRight, $iBottom) Local $aResult = DllCall("gdi32.dll", "int", "Rectangle", "hwnd", $hDC, "int", $iLeft, "int", $iTop, "int", $iRight, "int", $iBottom) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] <> 0 EndFunc ;==>_WinAPI_Rectangle_Pts Func _onExit() _WinAPI_DeleteObject($hPen) _WinAPI_DeleteObject($hBrushSel) _WinAPI_DeleteObject($hBrushNorm) Exit EndFunc ;==>_onExit Func _customcombobox_cleanup() _WinAPI_RemoveWindowSubclass($hGUI, $pwindowcallback, 1) DllCallbackFree($windowcallback) EndFunc ;==>_customcombobox_cleanup One more thing. The risk of a user of your UDF also want to use WM_MEASUREITEM & WM_DRAWITEM messages in his own code is probably very small. So I think you can use GUIRegisterMsg in your UDF for these two messages. But if you want to be on the completely safe side you can use subclassing. Edited February 17, 2017 by LarsJ One more thing 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 Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted February 17, 2017 Moderators Share Posted February 17, 2017 kurtykurtyboy, If you do decide to go with GUIRegisterMsg, you can always use a UDF function to register (or not) the message(s) you need and provide separate individual handler(s) which the user can add to his own handler if it already exists. Take a look at some of my UDFs (links hidden in my sig) to see how I did it - GUIListViewEx and ChooseFileFolder are good examples. I still hope that one day we will get the ability to chain messages in core code, but I am not holding my breath. I have tried several times to introduce a standard UDF to do it but the resistance from others was too strong. M23 kcvinu 1 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
kurtykurtyboy Posted February 17, 2017 Author Share Posted February 17, 2017 @LarsJ I would like to change how the combobox itself looks as well. As I understand it, the combobox handles its own WM_PAINT; so I need to subclass the combobox to handle WM_PAINT messages. Is there a better way to do that in AutoIT? Starting from your code above, I added my subclass / WM_PAINT code. It looks ok until you mouse over the items quickly. expandcollapse popup#include <GUIComboBox.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPIShellEx.au3> #include <WinAPIGdi.au3> Global Const $clrWindowText = 0xEEEEEE Global Const $clrHighlightText = 0xFFFFFF Global Const $clrHighlight = 0x777777 Global Const $clrWindow = 0x444444 Global $hBrushNorm = _WinAPI_CreateSolidBrush($clrWindow) Global $hBrushSel = _WinAPI_CreateSolidBrush($clrHighlight) Global $hPen = _WinAPI_CreatePen($PS_SOLID, 2, $clrHighlight) Global $hGUI, $ComboBox, $hComboBox Global $windowcallback, $pwindowcallback, $combocallback, $pcombocallback Global $comboOpen, $comboChanged, $comboHover OnAutoItExitRegister("_customcombobox_cleanup") ; OnAutoItExitRegister should be placed here, not in the function <<<<<<<<<<<<<<<<<<<<< Example() Func Example() $hGUI = GUICreate('Test', 220, 300) ;subclass the GUI ; The subclass must be implemented here to be able to respond to $WM_MEASUREITEM messages when the Combobox is created $windowcallback = DllCallbackRegister("_customCombobox_WindowCallback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pwindowcallback = DllCallbackGetPtr($windowcallback) _WinAPI_SetWindowSubclass($hGUI, $pwindowcallback, 1, 0) ; $iSubclassId = 1, $pData = 0 ($ComboBox is not available) $ComboBox = GUICtrlCreateCombo('', 10, 10, 200, 300, BitOR($WS_CHILD, $CBS_OWNERDRAWVARIABLE, $CBS_HASSTRINGS, $CBS_DROPDOWNLIST)) GUICtrlSetData($ComboBox, "Apple|Banana|Orange|Pear|Plum|WatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelon") $hComboBox = GUICtrlGetHandle($ComboBox) ;subclass combobox for WM_PAINT $combocallback = DllCallbackRegister("_customCombobox_Callback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pcombocallback = DllCallbackGetPtr($combocallback) _WinAPI_SetWindowSubclass(GUICtrlGetHandle($ComboBox), $pcombocallback, 2, $ComboBox) ; $iSubclassId = 2, $pData = CtrlID GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _onExit() EndSwitch WEnd EndFunc ;==>Example ;callback function for combobox Func _customCombobox_Callback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) Local $tPAINTSTRUCT, $hDC ;~ ConsoleWrite(Hex($iMsg)&@CRLF) If $iMsg == $WM_PAINT Then ;within the scope of this function, 'background' refers to the space between the selected item and the border $buttonWidth = _WinAPI_GetSystemMetrics($SM_CXVSCROLL) $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT) ;Get client rect $cRect = _WinAPI_GetClientRect($hWnd) ;shrink rect by 3 (space for border and background) _WinAPI_InflateRect ( $cRect, -3, -3 ) ;shrink right side (space for button) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")-$buttonWidth-1) ;remove border, button, and background from clipping region _WinAPI_IntersectClipRect ( $hDC, $cRect ) ;draw the default combobox using the $hDC DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $hDC, "lparam", $lParam ) ;remove the clipping region _WinAPI_SelectClipRgn ( $hDC, Null ) ;get area between middle and border (our background) $cRect = _WinAPI_GetClientRect($hWnd) _WinAPI_InflateRect ( $cRect, -1, -1 ) _WinAPI_IntersectClipRect ( $hDC, $cRect ) _WinAPI_InflateRect ( $cRect, -2, -2 ) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")+1) _WinAPI_ExcludeClipRect($hDC, $cRect) _WinAPI_InflateRect ( $cRect, 2, 2 ) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")-1-$buttonWidth-3) ;draw the background _WinAPI_FillRect($hDC, DllStructGetPtr($cRect), $hBrushNorm) ;reset the clipping region again _WinAPI_SelectClipRgn ( $hDC, Null ) ;get the rect for the button and set clipping region $bRect = _WinAPI_GetClientRect($hWnd) DllStructSetData($bRect, "Left", DllStructGetData($bRect, "Right")-1-$buttonWidth-3) DllStructSetData($bRect, "Right", DllStructGetData($bRect, "Right")-1) DllStructSetData($bRect, "Top", DllStructGetData($bRect, "Top")+1) DllStructSetData($bRect, "Bottom", DllStructGetData($bRect, "Bottom")-1) _WinAPI_IntersectClipRect ( $hDC, $bRect ) ;draw the button If $comboHover Then $buttonColor = 0xABABAB $borderColor = 0x222222 $arrowColor = 0xEFEFEF Else $buttonColor = 0x858585 $borderColor = 0x222222 $arrowColor = 0xBBBBBB EndIf $hBrushButton = _WinAPI_CreateSolidBrush($buttonColor) _WinAPI_FillRect($hDC, DllStructGetPtr($bRect), $hBrushNorm) ;Create the arrow path $leftpos = DllStructGetData($bRect, "Left") $toppos = DllStructGetData($bRect, "Top") $buttonMiddle = ($buttonWidth+3)/2 $buttonVMiddle = (DllStructGetData($cRect, "Bottom")-$toppos)/2 $arrowWidth = 12 $arrowHeight = 12 _WinAPI_BeginPath($hDC) _WinAPI_MoveTo($hDC, $leftpos+$buttonMiddle, $toppos+$buttonVMiddle+$arrowHeight/2) _WinAPI_LineTo($hDC, $leftpos+$buttonMiddle+$arrowWidth/2, $toppos++$buttonVMiddle-$arrowHeight/2) _WinAPI_LineTo($hDC, $leftpos+$buttonMiddle-$arrowWidth/2, $toppos++$buttonVMiddle-$arrowHeight/2) _WinAPI_CloseFigure($hDC) _WinAPI_EndPath($hDC) ;draw the arrow $hOldBrush = _WinAPI_SelectObject($hDC, _WinAPI_GetStockObject($DC_BRUSH)) $hOldPen = _WinAPI_SelectObject($hDC, _WinAPI_GetStockObject($DC_PEN)) _WinAPI_SetDCBrushColor($hDC, $arrowColor) _WinAPI_SetDCPenColor($hDC, 0x222222) _WinAPI_StrokeAndFillPath($hDC) _WinAPI_SelectObject($hDC, $hOldBrush) _WinAPI_SelectObject($hDC, $hOldPen) ;remove the clipping region _WinAPI_SelectClipRgn ( $hDC, Null ) ;remove inside from clipping region (keep only the border) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")+_WinAPI_GetSystemMetrics($SM_CXVSCROLL)+3) _WinAPI_ExcludeClipRect($hDC, $cRect) $cRect = _WinAPI_GetClientRect($hWnd) ;create border brush $hBrushBorder = _WinAPI_CreateSolidBrush($borderColor) ;get full rect size again and fill border $cRect = _WinAPI_GetClientRect($hWnd) _WinAPI_FillRect($hDC, DllStructGetPtr($cRect), $hBrushBorder) ;clean up _WinAPI_DeleteObject($hBrushBorder) _WinAPI_DeleteObject($hBrushButton) _WinAPI_DeleteObject($hOldBrush) _WinAPI_DeleteObject($hOldPen) _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT) Return 0 ElseIf $iMsg == $WM_MOUSEFIRST Then $comboHover = 1 ElseIf $iMsg == $WM_MOUSELEAVE Then $comboHover = 0 EndIf ;run default subclass proc Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] EndFunc ;==>_customCombobox_Callback ;subclassed window function --> GUI Func _customCombobox_WindowCallback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) If $iMsg <> $WM_DRAWITEM And $iMsg <> $WM_MEASUREITEM Then Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] Local Const $ODT_COMBOBOX = 3 Local Const $ODT_STATIC = 5 Local Const $ODS_SELECTED = 1 Local Const $ODS_COMBOBOXEDIT = 4096 Switch $iMsg Case $WM_DRAWITEM Local Const $tagDRAWITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemAction;' & _ 'uint itemState;' & _ 'hwnd hwndItem;' & _ 'hwnd hDC;' & _ $tagRECT & _ ';ulong_ptr itemData;' Local $tDIS Local $iCtlType, $iCtlID, $iItemID, $iItemAction, $iItemState Local $clrForeground, $clrBackground Local $hWndItem, $hDC, $hOldPen, $hOldBrush Local $tRect, $aRect[4] Local $sText, $iCode, $iIDFrom $tDIS = DllStructCreate($tagDRAWITEMSTRUCT, $lParam) $iCtlType = DllStructGetData($tDIS, 'CtlType') $iCtlID = DllStructGetData($tDIS, 'CtlID') $iItemID = DllStructGetData($tDIS, 'itemID') $iItemAction = DllStructGetData($tDIS, 'itemAction') $iItemState = DllStructGetData($tDIS, 'itemState') $hWndItem = DllStructGetData($tDIS, 'hwndItem') $hDC = DllStructGetData($tDIS, 'hDC') $tRect = DllStructCreate($tagRECT) If $iCtlType = $ODT_COMBOBOX Then If $iCtlID = $ComboBox Then ;if my combobox For $i = 1 To 4 DllStructSetData($tRect, $i, DllStructGetData($tDIS, $i + 7)) $aRect[$i - 1] = DllStructGetData($tRect, $i) Next _GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) If BitAND($iItemState, $ODS_SELECTED) And Not BitAND($iItemState, $ODS_COMBOBOXEDIT) Then $hOldBrush = _WinAPI_SelectObject($hDC, $hBrushSel) $hOldPen = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_Rectangle_Pts($hDC, $aRect[0] + 1, $aRect[1] + 1, $aRect[2], $aRect[3]) _WinAPI_SelectObject($hDC, $hOldPen) _WinAPI_SelectObject($hDC, $hOldBrush) $clrForeground = _WinAPI_SetTextColor($hDC, $clrHighlightText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrHighlight) Else $clrForeground = _WinAPI_SetTextColor($hDC, $clrWindowText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrWindow) _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrushNorm) EndIf DllStructSetData($tRect, "Left", $aRect[0] + 4) DllStructSetData($tRect, "Top", $aRect[1] + 4) DllStructSetData($tRect, "Bottom", $aRect[3] - 2) _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_VCENTER)) _WinAPI_SetTextColor($hDC, $clrForeground) _WinAPI_SetBkColor($hDC, $clrBackground) Return True EndIf EndIf Case $WM_MEASUREITEM Local Const $tagMEASUREITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemWidth;' & _ 'uint itemHeight;' & _ 'ulong_ptr itemData;' Local $tMIS = DllStructCreate($tagMEASUREITEMSTRUCT, $lparam) Local $iCtlType, $iCtlID, $iItemID, $iItemWidth, $iItemHeight Local $hComboBox Local $tSize Local $sText $iCtlType = DllStructGetData($tMIS, 'CtlType') $iCtlID = DllStructGetData($tMIS, 'CtlID') $iItemID = DllStructGetData($tMIS, 'itemID') $iItemWidth = DllStructGetData($tMIS, 'itemWidth') $iItemHeight = DllStructGetData($tMIS, 'itemHeight') $hComboBox = GUICtrlGetHandle($iCtlID) If $iCtlType = $ODT_COMBOBOX Then DllStructSetData($tMIS, 'itemHeight', 30) ; 20 -> 30 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< EndIf EndSwitch ;default subclass proc Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] EndFunc ;==>_customCombobox_WindowCallback Func _WinAPI_Rectangle_Pts($hDC, $iLeft, $iTop, $iRight, $iBottom) Local $aResult = DllCall("gdi32.dll", "int", "Rectangle", "hwnd", $hDC, "int", $iLeft, "int", $iTop, "int", $iRight, "int", $iBottom) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] <> 0 EndFunc ;==>_WinAPI_Rectangle_Pts Func _onExit() _WinAPI_DeleteObject($hPen) _WinAPI_DeleteObject($hBrushSel) _WinAPI_DeleteObject($hBrushNorm) Exit EndFunc ;==>_onExit Func _customcombobox_cleanup() _WinAPI_RemoveWindowSubclass($hGUI, $pwindowcallback, 1) DllCallbackFree($windowcallback) EndFunc ;==>_customcombobox_cleanup Link to comment Share on other sites More sharing options...
kurtykurtyboy Posted February 17, 2017 Author Share Posted February 17, 2017 I realized I could simplify this by hiding the combobox and using a label (or anything really!) instead. Then I just need to add a few things to open the list on click ... GUICtrlSetState($ComboBox, 32) ;hide combobox $label = GUICtrlCreateLabel("combo", 10, 10, 200, 30) GUICtrlSetBkColor(-1, 0x555555) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _onExit() Case $label ConsoleWrite("hit"&@CRLF) _showComboList() EndSwitch WEnd ... Func _showComboList() _GUICtrlComboBox_ShowDropDown($ComboBox, True) EndFunc This needs some tweaking of course, but I think it is an acceptable solution (in my case) to keep moving forward. Melba23, I like your suggestion and will probably use it for this. Thanks for the tip! I am still curious, though, if my original idea is possible with WM_PAINT... Link to comment Share on other sites More sharing options...
LarsJ Posted February 17, 2017 Share Posted February 17, 2017 There is no doubt that it's WM_PAINT messages from the rest of the GUI (WM_PAINT messages that does not belong to the Combo Box) that's causing the flicker. If you implement WM_MEASUREITEM and WM_DRAWITEM messages with GUIRegisterMsg and WM_PAINT messages from the Combo Box through subclassing everything is working: expandcollapse popup#include <GUIComboBox.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPIShellEx.au3> #include <WinAPIGdi.au3> Global Const $clrWindowText = 0xEEEEEE Global Const $clrHighlightText = 0xFFFFFF Global Const $clrHighlight = 0x777777 Global Const $clrWindow = 0x444444 Global $hBrushNorm = _WinAPI_CreateSolidBrush($clrWindow) Global $hBrushSel = _WinAPI_CreateSolidBrush($clrHighlight) Global $hPen = _WinAPI_CreatePen($PS_SOLID, 2, $clrHighlight) Global $hGUI, $ComboBox, $hComboBox Global $windowcallback, $pwindowcallback, $combocallback, $pcombocallback Global $comboOpen, $comboChanged, $comboHover OnAutoItExitRegister("_customcombobox_cleanup") ; OnAutoItExitRegister should be placed here, not in the function <<<<<<<<<<<<<<<<<<<<< Example() Func Example() GUIRegisterMsg($WM_MEASUREITEM, '_WM_MEASUREITEM') GUIRegisterMsg($WM_DRAWITEM, '_WM_DRAWITEM') $hGUI = GUICreate('Test', 220, 300) $ComboBox = GUICtrlCreateCombo('', 10, 10, 200, 300, BitOR($WS_CHILD, $CBS_OWNERDRAWVARIABLE, $CBS_HASSTRINGS, $CBS_DROPDOWNLIST)) GUICtrlSetData($ComboBox, "Apple|Banana|Orange|Pear|Plum|WatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelon") $hComboBox = GUICtrlGetHandle($ComboBox) ;subclass combobox for WM_PAINT $combocallback = DllCallbackRegister("_customCombobox_Callback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pcombocallback = DllCallbackGetPtr($combocallback) _WinAPI_SetWindowSubclass(GUICtrlGetHandle($ComboBox), $pcombocallback, 2, $ComboBox) ; $iSubclassId = 2, $pData = CtrlID GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _onExit() EndSwitch WEnd EndFunc ;==>Example ;callback function for combobox Func _customCombobox_Callback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) Local $tPAINTSTRUCT, $hDC ;~ ConsoleWrite(Hex($iMsg)&@CRLF) If $iMsg == $WM_PAINT Then ;within the scope of this function, 'background' refers to the space between the selected item and the border $buttonWidth = _WinAPI_GetSystemMetrics($SM_CXVSCROLL) $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT) ;Get client rect $cRect = _WinAPI_GetClientRect($hWnd) ;shrink rect by 3 (space for border and background) _WinAPI_InflateRect ( $cRect, -3, -3 ) ;shrink right side (space for button) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")-$buttonWidth-1) ;remove border, button, and background from clipping region _WinAPI_IntersectClipRect ( $hDC, $cRect ) ;draw the default combobox using the $hDC DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $hDC, "lparam", $lParam ) ;remove the clipping region _WinAPI_SelectClipRgn ( $hDC, Null ) ;get area between middle and border (our background) $cRect = _WinAPI_GetClientRect($hWnd) _WinAPI_InflateRect ( $cRect, -1, -1 ) _WinAPI_IntersectClipRect ( $hDC, $cRect ) _WinAPI_InflateRect ( $cRect, -2, -2 ) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")+1) _WinAPI_ExcludeClipRect($hDC, $cRect) _WinAPI_InflateRect ( $cRect, 2, 2 ) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")-1-$buttonWidth-3) ;draw the background _WinAPI_FillRect($hDC, DllStructGetPtr($cRect), $hBrushNorm) ;reset the clipping region again _WinAPI_SelectClipRgn ( $hDC, Null ) ;get the rect for the button and set clipping region $bRect = _WinAPI_GetClientRect($hWnd) DllStructSetData($bRect, "Left", DllStructGetData($bRect, "Right")-1-$buttonWidth-3) DllStructSetData($bRect, "Right", DllStructGetData($bRect, "Right")-1) DllStructSetData($bRect, "Top", DllStructGetData($bRect, "Top")+1) DllStructSetData($bRect, "Bottom", DllStructGetData($bRect, "Bottom")-1) _WinAPI_IntersectClipRect ( $hDC, $bRect ) ;draw the button If $comboHover Then $buttonColor = 0xABABAB $borderColor = 0x222222 $arrowColor = 0xEFEFEF Else $buttonColor = 0x858585 $borderColor = 0x222222 $arrowColor = 0xBBBBBB EndIf $hBrushButton = _WinAPI_CreateSolidBrush($buttonColor) _WinAPI_FillRect($hDC, DllStructGetPtr($bRect), $hBrushNorm) ;Create the arrow path $leftpos = DllStructGetData($bRect, "Left") $toppos = DllStructGetData($bRect, "Top") $buttonMiddle = ($buttonWidth+3)/2 $buttonVMiddle = (DllStructGetData($cRect, "Bottom")-$toppos)/2 $arrowWidth = 12 $arrowHeight = 12 _WinAPI_BeginPath($hDC) _WinAPI_MoveTo($hDC, $leftpos+$buttonMiddle, $toppos+$buttonVMiddle+$arrowHeight/2) _WinAPI_LineTo($hDC, $leftpos+$buttonMiddle+$arrowWidth/2, $toppos++$buttonVMiddle-$arrowHeight/2) _WinAPI_LineTo($hDC, $leftpos+$buttonMiddle-$arrowWidth/2, $toppos++$buttonVMiddle-$arrowHeight/2) _WinAPI_CloseFigure($hDC) _WinAPI_EndPath($hDC) ;draw the arrow $hOldBrush = _WinAPI_SelectObject($hDC, _WinAPI_GetStockObject($DC_BRUSH)) $hOldPen = _WinAPI_SelectObject($hDC, _WinAPI_GetStockObject($DC_PEN)) _WinAPI_SetDCBrushColor($hDC, $arrowColor) _WinAPI_SetDCPenColor($hDC, 0x222222) _WinAPI_StrokeAndFillPath($hDC) _WinAPI_SelectObject($hDC, $hOldBrush) _WinAPI_SelectObject($hDC, $hOldPen) ;remove the clipping region _WinAPI_SelectClipRgn ( $hDC, Null ) ;remove inside from clipping region (keep only the border) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")+_WinAPI_GetSystemMetrics($SM_CXVSCROLL)+3) _WinAPI_ExcludeClipRect($hDC, $cRect) $cRect = _WinAPI_GetClientRect($hWnd) ;create border brush $hBrushBorder = _WinAPI_CreateSolidBrush($borderColor) ;get full rect size again and fill border $cRect = _WinAPI_GetClientRect($hWnd) _WinAPI_FillRect($hDC, DllStructGetPtr($cRect), $hBrushBorder) ;clean up _WinAPI_DeleteObject($hBrushBorder) _WinAPI_DeleteObject($hBrushButton) _WinAPI_DeleteObject($hOldBrush) _WinAPI_DeleteObject($hOldPen) _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT) Return 0 ElseIf $iMsg == $WM_MOUSEFIRST Then $comboHover = 1 ElseIf $iMsg == $WM_MOUSELEAVE Then $comboHover = 0 EndIf ;run default subclass proc Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] EndFunc ;==>_customCombobox_Callback ;guiregistermsg WM_DRAWITEM Func _WM_DRAWITEM($hWnd, $iMsg, $iwParam, $ilParam) Local Const $tagDRAWITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemAction;' & _ 'uint itemState;' & _ 'hwnd hwndItem;' & _ 'hwnd hDC;' & _ $tagRECT & _ ';ulong_ptr itemData;' Local Const $ODT_COMBOBOX = 3 Local Const $ODS_SELECTED = 1 Local Const $ODS_COMBOBOXEDIT = 4096 Local $tDIS = DllStructCreate($tagDRAWITEMSTRUCT, $ilParam) Local $iCtlType, $iCtlID, $iItemID, $iItemAction, $iItemState Local $clrForeground, $clrBackground Local $hWndItem, $hDC, $hOldPen, $hOldBrush Local $tRect, $aRect[4] Local $sText $iCtlType = DllStructGetData($tDIS, 'CtlType') $iCtlID = DllStructGetData($tDIS, 'CtlID') $iItemID = DllStructGetData($tDIS, 'itemID') $iItemAction = DllStructGetData($tDIS, 'itemAction') $iItemState = DllStructGetData($tDIS, 'itemState') $hWndItem = DllStructGetData($tDIS, 'hwndItem') $hDC = DllStructGetData($tDIS, 'hDC') $tRect = DllStructCreate($tagRECT) If $iCtlType = $ODT_COMBOBOX Then Switch $iCtlID Case $ComboBox For $i = 1 To 4 DllStructSetData($tRect, $i, DllStructGetData($tDIS, $i + 7)) $aRect[$i - 1] = DllStructGetData($tRect, $i) Next _GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) If BitAND($iItemState, $ODS_SELECTED) And Not BitAND($iItemState, $ODS_COMBOBOXEDIT) Then $hOldBrush = _WinAPI_SelectObject($hDC, $hBrushSel) $hOldPen = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_Rectangle_Pts($hDC, $aRect[0] + 1, $aRect[1] + 1, $aRect[2], $aRect[3]) _WinAPI_SelectObject($hDC, $hOldPen) _WinAPI_SelectObject($hDC, $hOldBrush) $clrForeground = _WinAPI_SetTextColor($hDC, $clrHighlightText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrHighlight) Else $clrForeground = _WinAPI_SetTextColor($hDC, $clrWindowText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrWindow) _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrushNorm) EndIf DllStructSetData($tRect, "Left", $aRect[0] + 4) DllStructSetData($tRect, "Top", $aRect[1] + 4) DllStructSetData($tRect, "Bottom", $aRect[3] - 2) _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_VCENTER)) _WinAPI_SetTextColor($hDC, $clrForeground) _WinAPI_SetBkColor($hDC, $clrBackground) EndSwitch EndIf Return $GUI_RUNDEFMSG EndFunc ;==>_WM_DRAWITEM ;guiregistermsg WM_MEASUREITEM Func _WM_MEASUREITEM($hWnd, $iMsg, $iwParam, $ilParam) Local Const $tagMEASUREITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemWidth;' & _ 'uint itemHeight;' & _ 'ulong_ptr itemData;' Local Const $ODT_COMBOBOX = 3 Local $tMIS = DllStructCreate($tagMEASUREITEMSTRUCT, $ilParam) Local $iCtlType, $iCtlID, $iItemID, $iItemWidth, $iItemHeight Local $hComboBox Local $tSize Local $sText $iCtlType = DllStructGetData($tMIS, 'CtlType') $iCtlID = DllStructGetData($tMIS, 'CtlID') $iItemID = DllStructGetData($tMIS, 'itemID') $iItemWidth = DllStructGetData($tMIS, 'itemWidth') $iItemHeight = DllStructGetData($tMIS, 'itemHeight') $hComboBox = GUICtrlGetHandle($iCtlID) If $iCtlType = $ODT_COMBOBOX Then DllStructSetData($tMIS, 'itemHeight', 30) ; 20 -> 30 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< EndIf Return $GUI_RUNDEFMSG EndFunc ;==>_WM_MEASUREITEM Func _WinAPI_Rectangle_Pts($hDC, $iLeft, $iTop, $iRight, $iBottom) Local $aResult = DllCall("gdi32.dll", "int", "Rectangle", "hwnd", $hDC, "int", $iLeft, "int", $iTop, "int", $iRight, "int", $iBottom) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] <> 0 EndFunc ;==>_WinAPI_Rectangle_Pts Func _onExit() _WinAPI_DeleteObject($hPen) _WinAPI_DeleteObject($hBrushSel) _WinAPI_DeleteObject($hBrushNorm) Exit EndFunc ;==>_onExit Func _customcombobox_cleanup() GUIRegisterMsg($WM_MEASUREITEM, '') GUIRegisterMsg($WM_DRAWITEM, '') EndFunc ;==>_customcombobox_cleanup Nice combobox. 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 Link to comment Share on other sites More sharing options...
LarsJ Posted February 17, 2017 Share Posted February 17, 2017 I've figured it out. This command interacts too much with your ComboBox at the same time as you are owner-drawing it: _GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) If you extracts the texts from an array it's working: expandcollapse popup#include <GUIComboBox.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPIShellEx.au3> #include <WinAPIGdi.au3> Global Const $clrWindowText = 0xEEEEEE Global Const $clrHighlightText = 0xFFFFFF Global Const $clrHighlight = 0x777777 Global Const $clrWindow = 0x444444 Global $hBrushNorm = _WinAPI_CreateSolidBrush($clrWindow) Global $hBrushSel = _WinAPI_CreateSolidBrush($clrHighlight) Global $hPen = _WinAPI_CreatePen($PS_SOLID, 2, $clrHighlight) Global $hGUI, $ComboBox, $hComboBox Global $windowcallback, $pwindowcallback, $combocallback, $pcombocallback Global $comboOpen, $comboChanged, $comboHover Global $aTexts = [ "Apple", "Banana", "Orange", "Pear", "Plum", "WatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelon" ] OnAutoItExitRegister("_customcombobox_cleanup") ; OnAutoItExitRegister should be placed here, not in the function <<<<<<<<<<<<<<<<<<<<< Example() Func Example() $hGUI = GUICreate('Test', 220, 300) ;subclass the GUI ; The subclass must be implemented here to be able to respond to $WM_MEASUREITEM messages when the Combobox is created $windowcallback = DllCallbackRegister("_customCombobox_WindowCallback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pwindowcallback = DllCallbackGetPtr($windowcallback) _WinAPI_SetWindowSubclass($hGUI, $pwindowcallback, 1, 0) ; $iSubclassId = 1, $pData = 0 ($ComboBox is not available) $ComboBox = GUICtrlCreateCombo('', 10, 10, 200, 300, BitOR($WS_CHILD, $CBS_OWNERDRAWVARIABLE, $CBS_HASSTRINGS, $CBS_DROPDOWNLIST)) GUICtrlSetData($ComboBox, "Apple|Banana|Orange|Pear|Plum|WatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelonWatermelon") $hComboBox = GUICtrlGetHandle($ComboBox) ;subclass combobox for WM_PAINT $combocallback = DllCallbackRegister("_customCombobox_Callback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr") $pcombocallback = DllCallbackGetPtr($combocallback) _WinAPI_SetWindowSubclass(GUICtrlGetHandle($ComboBox), $pcombocallback, 2, $ComboBox) ; $iSubclassId = 2, $pData = CtrlID GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _onExit() EndSwitch WEnd EndFunc ;==>Example ;callback function for combobox Func _customCombobox_Callback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) Local $tPAINTSTRUCT, $hDC ;~ ConsoleWrite(Hex($iMsg)&@CRLF) If $iMsg == $WM_PAINT Then ;within the scope of this function, 'background' refers to the space between the selected item and the border $buttonWidth = _WinAPI_GetSystemMetrics($SM_CXVSCROLL) $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT) ;Get client rect $cRect = _WinAPI_GetClientRect($hWnd) ;shrink rect by 3 (space for border and background) _WinAPI_InflateRect ( $cRect, -3, -3 ) ;shrink right side (space for button) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")-$buttonWidth-1) ;remove border, button, and background from clipping region _WinAPI_IntersectClipRect ( $hDC, $cRect ) ;draw the default combobox using the $hDC DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $hDC, "lparam", $lParam ) ;remove the clipping region _WinAPI_SelectClipRgn ( $hDC, Null ) ;get area between middle and border (our background) $cRect = _WinAPI_GetClientRect($hWnd) _WinAPI_InflateRect ( $cRect, -1, -1 ) _WinAPI_IntersectClipRect ( $hDC, $cRect ) _WinAPI_InflateRect ( $cRect, -2, -2 ) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")+1) _WinAPI_ExcludeClipRect($hDC, $cRect) _WinAPI_InflateRect ( $cRect, 2, 2 ) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")-1-$buttonWidth-3) ;draw the background _WinAPI_FillRect($hDC, DllStructGetPtr($cRect), $hBrushNorm) ;reset the clipping region again _WinAPI_SelectClipRgn ( $hDC, Null ) ;get the rect for the button and set clipping region $bRect = _WinAPI_GetClientRect($hWnd) DllStructSetData($bRect, "Left", DllStructGetData($bRect, "Right")-1-$buttonWidth-3) DllStructSetData($bRect, "Right", DllStructGetData($bRect, "Right")-1) DllStructSetData($bRect, "Top", DllStructGetData($bRect, "Top")+1) DllStructSetData($bRect, "Bottom", DllStructGetData($bRect, "Bottom")-1) _WinAPI_IntersectClipRect ( $hDC, $bRect ) ;draw the button If $comboHover Then $buttonColor = 0xABABAB $borderColor = 0x222222 $arrowColor = 0xEFEFEF Else $buttonColor = 0x858585 $borderColor = 0x222222 $arrowColor = 0xBBBBBB EndIf $hBrushButton = _WinAPI_CreateSolidBrush($buttonColor) _WinAPI_FillRect($hDC, DllStructGetPtr($bRect), $hBrushNorm) ;Create the arrow path $leftpos = DllStructGetData($bRect, "Left") $toppos = DllStructGetData($bRect, "Top") $buttonMiddle = ($buttonWidth+3)/2 $buttonVMiddle = (DllStructGetData($cRect, "Bottom")-$toppos)/2 $arrowWidth = 12 $arrowHeight = 12 _WinAPI_BeginPath($hDC) _WinAPI_MoveTo($hDC, $leftpos+$buttonMiddle, $toppos+$buttonVMiddle+$arrowHeight/2) _WinAPI_LineTo($hDC, $leftpos+$buttonMiddle+$arrowWidth/2, $toppos++$buttonVMiddle-$arrowHeight/2) _WinAPI_LineTo($hDC, $leftpos+$buttonMiddle-$arrowWidth/2, $toppos++$buttonVMiddle-$arrowHeight/2) _WinAPI_CloseFigure($hDC) _WinAPI_EndPath($hDC) ;draw the arrow $hOldBrush = _WinAPI_SelectObject($hDC, _WinAPI_GetStockObject($DC_BRUSH)) $hOldPen = _WinAPI_SelectObject($hDC, _WinAPI_GetStockObject($DC_PEN)) _WinAPI_SetDCBrushColor($hDC, $arrowColor) _WinAPI_SetDCPenColor($hDC, 0x222222) _WinAPI_StrokeAndFillPath($hDC) _WinAPI_SelectObject($hDC, $hOldBrush) _WinAPI_SelectObject($hDC, $hOldPen) ;remove the clipping region _WinAPI_SelectClipRgn ( $hDC, Null ) ;remove inside from clipping region (keep only the border) DllStructSetData($cRect, "Right", DllStructGetData($cRect, "Right")+_WinAPI_GetSystemMetrics($SM_CXVSCROLL)+3) _WinAPI_ExcludeClipRect($hDC, $cRect) $cRect = _WinAPI_GetClientRect($hWnd) ;create border brush $hBrushBorder = _WinAPI_CreateSolidBrush($borderColor) ;get full rect size again and fill border $cRect = _WinAPI_GetClientRect($hWnd) _WinAPI_FillRect($hDC, DllStructGetPtr($cRect), $hBrushBorder) ;clean up _WinAPI_DeleteObject($hBrushBorder) _WinAPI_DeleteObject($hBrushButton) _WinAPI_DeleteObject($hOldBrush) _WinAPI_DeleteObject($hOldPen) _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT) Return 0 ElseIf $iMsg == $WM_MOUSEFIRST Then $comboHover = 1 ElseIf $iMsg == $WM_MOUSELEAVE Then $comboHover = 0 EndIf ;run default subclass proc Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] EndFunc ;==>_customCombobox_Callback ;subclassed window function --> GUI Func _customCombobox_WindowCallback($hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData) If $iMsg <> $WM_DRAWITEM And $iMsg <> $WM_MEASUREITEM Then Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] Local Const $ODT_COMBOBOX = 3 Local Const $ODT_STATIC = 5 Local Const $ODS_SELECTED = 1 Local Const $ODS_COMBOBOXEDIT = 4096 Switch $iMsg Case $WM_DRAWITEM Local Const $tagDRAWITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemAction;' & _ 'uint itemState;' & _ 'hwnd hwndItem;' & _ 'hwnd hDC;' & _ $tagRECT & _ ';ulong_ptr itemData;' Local $tDIS Local $iCtlType, $iCtlID, $iItemID, $iItemAction, $iItemState Local $clrForeground, $clrBackground Local $hWndItem, $hDC, $hOldPen, $hOldBrush Local $tRect, $aRect[4] Local $sText, $iCode, $iIDFrom $tDIS = DllStructCreate($tagDRAWITEMSTRUCT, $lParam) $iCtlType = DllStructGetData($tDIS, 'CtlType') $iCtlID = DllStructGetData($tDIS, 'CtlID') $iItemID = DllStructGetData($tDIS, 'itemID') $iItemAction = DllStructGetData($tDIS, 'itemAction') $iItemState = DllStructGetData($tDIS, 'itemState') $hWndItem = DllStructGetData($tDIS, 'hwndItem') $hDC = DllStructGetData($tDIS, 'hDC') $tRect = DllStructCreate($tagRECT) If $iCtlType = $ODT_COMBOBOX Then If $iCtlID = $ComboBox Then ;if my combobox For $i = 1 To 4 DllStructSetData($tRect, $i, DllStructGetData($tDIS, $i + 7)) $aRect[$i - 1] = DllStructGetData($tRect, $i) Next ;_GUICtrlComboBox_GetLBText($hWndItem, $iItemID, $sText) $sText = $iItemID < 6 ? $aTexts[$iItemID] : "" If BitAND($iItemState, $ODS_SELECTED) And Not BitAND($iItemState, $ODS_COMBOBOXEDIT) Then $hOldBrush = _WinAPI_SelectObject($hDC, $hBrushSel) $hOldPen = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_Rectangle_Pts($hDC, $aRect[0] + 1, $aRect[1] + 1, $aRect[2], $aRect[3]) _WinAPI_SelectObject($hDC, $hOldPen) _WinAPI_SelectObject($hDC, $hOldBrush) $clrForeground = _WinAPI_SetTextColor($hDC, $clrHighlightText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrHighlight) Else $clrForeground = _WinAPI_SetTextColor($hDC, $clrWindowText) $clrBackground = _WinAPI_SetBkColor($hDC, $clrWindow) _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrushNorm) EndIf DllStructSetData($tRect, "Left", $aRect[0] + 4) DllStructSetData($tRect, "Top", $aRect[1] + 4) DllStructSetData($tRect, "Bottom", $aRect[3] - 2) _WinAPI_DrawText($hDC, $sText, $tRect, BitOR($DT_LEFT, $DT_VCENTER)) _WinAPI_SetTextColor($hDC, $clrForeground) _WinAPI_SetBkColor($hDC, $clrBackground) Return True EndIf EndIf Case $WM_MEASUREITEM Local Const $tagMEASUREITEMSTRUCT = _ 'uint CtlType;' & _ 'uint CtlID;' & _ 'uint itemID;' & _ 'uint itemWidth;' & _ 'uint itemHeight;' & _ 'ulong_ptr itemData;' Local $tMIS = DllStructCreate($tagMEASUREITEMSTRUCT, $lparam) Local $iCtlType, $iCtlID, $iItemID, $iItemWidth, $iItemHeight Local $hComboBox Local $tSize Local $sText $iCtlType = DllStructGetData($tMIS, 'CtlType') $iCtlID = DllStructGetData($tMIS, 'CtlID') $iItemID = DllStructGetData($tMIS, 'itemID') $iItemWidth = DllStructGetData($tMIS, 'itemWidth') $iItemHeight = DllStructGetData($tMIS, 'itemHeight') $hComboBox = GUICtrlGetHandle($iCtlID) If $iCtlType = $ODT_COMBOBOX Then DllStructSetData($tMIS, 'itemHeight', 30) ; 20 -> 30 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< EndIf EndSwitch ;default subclass proc Return DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0] EndFunc ;==>_customCombobox_WindowCallback Func _WinAPI_Rectangle_Pts($hDC, $iLeft, $iTop, $iRight, $iBottom) Local $aResult = DllCall("gdi32.dll", "int", "Rectangle", "hwnd", $hDC, "int", $iLeft, "int", $iTop, "int", $iRight, "int", $iBottom) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] <> 0 EndFunc ;==>_WinAPI_Rectangle_Pts Func _onExit() _WinAPI_DeleteObject($hPen) _WinAPI_DeleteObject($hBrushSel) _WinAPI_DeleteObject($hBrushNorm) Exit EndFunc ;==>_onExit Func _customcombobox_cleanup() _WinAPI_RemoveWindowSubclass($hGUI, $pwindowcallback, 1) DllCallbackFree($windowcallback) EndFunc ;==>_customcombobox_cleanup It has nothing to do with WM_PAINT messages. Sorry. 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 Link to comment Share on other sites More sharing options...
kurtykurtyboy Posted February 19, 2017 Author Share Posted February 19, 2017 LarsJ, what an amazing discovery! Thank you so much. Quick question... is it not necessary to do RemoveWindowSubclass and DllCallbackFree for the combobox callback? Func _customcombobox_cleanup() _WinAPI_RemoveWindowSubclass($hGUI, $pcombocallback, 1) DllCallbackFree($combocallback) _WinAPI_RemoveWindowSubclass($hGUI, $pwindowcallback, 1) DllCallbackFree($windowcallback) EndFunc ;==>_customcombobox_cleanup Link to comment Share on other sites More sharing options...
LarsJ Posted February 19, 2017 Share Posted February 19, 2017 You are welcome. You can leave the cleanup to AutoIt. But I would add the code in a UDF. Have you created a listbox scrollbar if you add more items? 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 Link to comment Share on other sites More sharing options...
kurtykurtyboy Posted February 20, 2017 Author Share Posted February 20, 2017 11 hours ago, LarsJ said: Have you created a listbox scrollbar if you add more items? To be honest, I haven't thought that far ahead.. Any pointers on where to start? Link to comment Share on other sites More sharing options...
LarsJ Posted February 20, 2017 Share Posted February 20, 2017 Make some Google searches. 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 Link to comment Share on other sites More sharing options...
ura Posted June 29, 2017 Share Posted June 29, 2017 (edited) I found a question! When you click the ComboBox, the cursor moves quickly to the list, and the item is drawn randomly Edited July 4, 2017 by ura Link to comment Share on other sites More sharing options...
ura Posted July 1, 2017 Share Posted July 1, 2017 (edited) Edited July 4, 2017 by ura Link to comment Share on other sites More sharing options...
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