You should not be so scared about the callback solution, because the callback function relates to the parent window.
What you have to do is, you need some configuration. I did that in a small structure and a map.
See the updated example based on breeze4me's update of my first example.
BTW: I think this is very good solution, because it works with mouse and keyboard. (IMHO)
Code: Select all
;
; File : ComboBox_AdvancedExample.pb
;
; ComboBox OwnerDraw in PureBasic
;
EnableExplicit
Enumeration EWindow 1
#WND_Main
EndEnumeration
Enumeration EGadget 1
#GDT_strInfo
#GDT_cbbValues
#GDT_cbbZipCodes ; of Florida ... why not!
EndEnumeration
#CB_GETCOMBOBOXINFO = 356
Structure COMBOBOXINFO Align #PB_Structure_AlignC
cbSize.l
rcItem.RECT
rcButton.RECT
stateButton.l
hwndCombo.i
hwndItem.i
hwndList.i
EndStructure
#COMBOBOX_COLUMN_0 = 60
#COMBOBOX_COLUMN_1 = 120
#COMBOBOX_COLUMN_2 = 120
; new user defined structure
;
Structure TComboBoxData
Separator$ ; i.e. #LF$, |, etc.
ListWndWidth.i ; listbox of the combobox
Array Columns.i(0) ;
; tbc.
EndStructure
; ---
Global NewMap AdvComboBoxData.TComboBoxData() ; Key is Str(Gadget)
Global NewList CodeDesc.s()
AddElement(CodeDesc()) : CodeDesc() = "PINEAPPLE"
AddElement(CodeDesc()) : CodeDesc() = "BANANA"
AddElement(CodeDesc()) : CodeDesc() = "DRAGON FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "STAR FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "MANGO"
AddElement(CodeDesc()) : CodeDesc() = "RAMBUTAN"
AddElement(CodeDesc()) : CodeDesc() = "WATERMELON"
AddElement(CodeDesc()) : CodeDesc() = "POMEGRANATE"
AddElement(CodeDesc()) : CodeDesc() = "LYCHEE"
AddElement(CodeDesc()) : CodeDesc() = "MANGOSTEEN"
AddElement(CodeDesc()) : CodeDesc() = "TAMARIND"
AddElement(CodeDesc()) : CodeDesc() = "PASSION FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "PAPAYA"
; ---
; ---
Procedure WindowCallback(hWnd, Msg, WParam, LParam)
Protected result, hBrush, *lpdis.DRAWITEMSTRUCT
Protected text$, t$, sep$, tlen, nn, width, dtFlags
Protected cbinfo.COMBOBOXINFO, rt.RECT ; resize the list of the combobox
Select Msg
Case #WM_DRAWITEM
*lpdis = lParam ; typecast to DRAWITEMSTRUCT
If *lpdis\CtlType = #ODT_COMBOBOX
; Change the listbox size of the combobox.
;
If IsGadget(WParam) And GadgetType(WParam) = #PB_GadgetType_ComboBox
cbinfo\cbSize = SizeOf(COMBOBOXINFO)
SendMessage_(GadgetID(WParam), #CB_GETCOMBOBOXINFO, 0, @cbinfo)
width = AdvComboBoxData(Str(WParam))\ListWndWidth
If cbinfo\hwndList
GetWindowRect_(cbinfo\hwndList, rt)
MoveWindow_(cbinfo\hwndList, rt\left, rt\top, DesktopScaledX(width), rt\bottom - rt\top, 0)
EndIf
EndIf
SetBkMode_(*lpdis\hDC, #TRANSPARENT) ; Text is rendered transparent
If *lpdis\ItemState & #ODS_FOCUS
hBrush = GetSysColorBrush_(#COLOR_HIGHLIGHT) ; returns a cached brush instead of allocating a new one
FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush)
SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_HIGHLIGHTTEXT))
; DrawFocusRect_(*lpdis\hDC, *lpdis\rcItem) ; <-- It's up to you. (I like it better this way)
Else
; Fill the background color differently for odd and even lines.
;
If *lpdis\itemID & 1 ; % 2 ; .. even or odd ??
hBrush = GetSysColorBrush_(#COLOR_WINDOW) ; this is borrowed from system, destroying is not needed.
FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush)
Else
hBrush = CreateSolidBrush_($FFF8F0) ; AliceBlue <-- my fav color.
FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush)
DeleteObject_(hBrush) ; here we need to destroy the created brush
EndIf
SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_WINDOWTEXT))
EndIf
If *lpdis\itemID <> -1
tlen = SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXTLEN, *lpdis\itemID, 0) ; lParam .. not used
If tlen > 0
text$ = Space(tlen)
SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXT, *lpdis\itemID, @text$)
EndIf
dtFlags = #DT_LEFT | #DT_VCENTER | #DT_END_ELLIPSIS
If Asc(text$)
sep$ = AdvComboBoxData(Str(WParam))\Separator$
; column 0
;
t$ = StringField(text$, 1, sep$) ; #LF$)
If Asc(t$)
*lpdis\rcItem\left + 5
DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags)
EndIf
; column 1 to N
;
For nn = 0 To ArraySize(AdvComboBoxData()\Columns()) - 1
t$ = StringField(text$, nn + 2, sep$) ; #LF$)
If Asc(t$)
*lpdis\rcItem\left + DesktopScaledX(AdvComboBoxData()\Columns(nn))
If *lpdis\itemState & #ODS_COMBOBOXEDIT = 0
Protected hPen, oldPen
; Draw a vertical divider for the column.
;
hPen = CreatePen_(#PS_SOLID, 1, $BBBBBB)
If hPen
oldPen = SelectObject_(*lpdis\hdc, hPen)
MoveToEx_(*lpdis\hdc, *lpdis\rcItem\left - 5, *lpdis\rcItem\top, 0)
LineTo_(*lpdis\hdc, *lpdis\rcItem\left - 5, *lpdis\rcItem\bottom)
SelectObject_(*lpdis\hdc, oldPen)
DeleteObject_(hPen)
EndIf
DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags)
EndIf
EndIf
Next nn
EndIf
EndIf
EndIf
; ProcedureReturn result
EndSelect
; default means handling by PB internally
;
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Procedure OpenMainWindow(WndW=400, WndH=100)
If OpenWindow(#WND_Main, 0, 0, WndW, WndH, "ComboBox OwnerDraw", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget | #PB_Window_TitleBar )
StringGadget(#GDT_strInfo, 8, 8, WndW-16, 20, "Selected Combobox item is .... ")
ComboBoxGadget(#GDT_cbbValues, 8, 40, 100, 21, #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
SendMessage_(GadgetID(#GDT_cbbValues), #CB_SETITEMHEIGHT, 0, DesktopScaledY(GadgetHeight(#GDT_cbbValues) - 2))
AdvComboBoxData(Str(#GDT_cbbValues))\Separator$ = #LF$
AdvComboBoxData()\ListWndWidth = 200 ; not scaled pixels
Dim AdvComboBoxData()\Columns(1)
AdvComboBoxData()\Columns(0) = #COMBOBOX_COLUMN_0
; NEW ComboBox useing the same callback .....
;
ComboBoxGadget(#GDT_cbbZipCodes, 8, 64, 100, 21, #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
SendMessage_(GadgetID(#GDT_cbbZipCodes), #CB_SETITEMHEIGHT, 0, DesktopScaledY(GadgetHeight(#GDT_cbbZipCodes) - 2)) ; ?? breezeme stuff!
AdvComboBoxData(Str(#GDT_cbbZipCodes))\Separator$ = #LF$ ;"|"
AdvComboBoxData()\ListWndWidth = 400 ; not scaled pixels
Dim AdvComboBoxData()\Columns(3)
AdvComboBoxData()\Columns(0) = #COMBOBOX_COLUMN_0
AdvComboBoxData()\Columns(1) = #COMBOBOX_COLUMN_1
AdvComboBoxData()\Columns(2) = #COMBOBOX_COLUMN_2
SetWindowCallback(@WindowCallback(), #WND_Main)
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure Main()
Protected index, text$
If OpenMainWindow()
ForEach(CodeDesc())
index + 1
; **
; ** Insert delimiter #LF$ to enable ComboBoxGadget value to be extracted without its
; ** associated description
; **
AddGadgetItem(#GDT_cbbValues, -1, "STK" + RSet(Str(index), 3, "0") + #LF$ + CodeDesc())
Next
; Copied from https://www.zip-codes.com/state/fl.asp
; ZIP Code LF City LF County LF Type
AddGadgetItem(#GDT_cbbZipCodes, -1, "32003" + #LF$ + "Fleming Island" + #LF$ + "Clay" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32004" + #LF$ + "Ponte Vedra Beach" + #LF$ + "Saint Johns" + #LF$ + "P.O. Box")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32006" + #LF$ + "Fleming Island" + #LF$ + "Clay" + #LF$ + "P.O. Box")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32007" + #LF$ + "Bostwick" + #LF$ + "Putnam" + #LF$ + "P.O. Box")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32008" + #LF$ + "Branford" + #LF$ + "Suwannee" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32009" + #LF$ + "Bryceville" + #LF$ + "Nassau" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32011" + #LF$ + "Callahan" + #LF$ + "Nassau" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32013" + #LF$ + "Day" + #LF$ + "Lafayette" + #LF$ + "P.O. Box")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32024" + #LF$ + "Lake City" + #LF$ + "Columbia" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32025" + #LF$ + "Lake City" + #LF$ + "Columbia" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32030" + #LF$ + "Doctors Inlet" + #LF$ + "Clay" + #LF$ + "P.O. Box")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32033" + #LF$ + "Elkton" + #LF$ + "Saint Johns" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32034" + #LF$ + "Fernandina Beach" + #LF$ + "Nassau" + #LF$ + "Standard")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32035" + #LF$ + "Fernandina Beach" + #LF$ + "Nassau" + #LF$ + "P.O. Box")
AddGadgetItem(#GDT_cbbZipCodes, -1, "32038" + #LF$ + "Fort White" + #LF$ + "Columbia" + #LF$ + "Standard")
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break ; bye
Case #PB_Event_Gadget
Select EventGadget()
Case #GDT_cbbValues
text$ = GetGadgetText(#GDT_cbbValues)
SetGadgetText(#GDT_strInfo, "Stock code == " + StringField(text$, 1, #LF$))
Case #GDT_cbbZipCodes
text$ = GetGadgetText(#GDT_cbbZipCodes)
Debug text$
EndSelect
EndSelect
ForEver
EndIf
ProcedureReturn 0
EndProcedure
End Main()