Back when Internet Explorer 3.0 was released a combobox control became available that has the ability to display images. The aim of this article is to show how to use one of these extended comboboxes, called ComboBoxEx, in a Liberty BASIC application without using a third party DLL, timer or callback.
Since this control is not native to Liberty BASIC it must be created and managed through the Windows API. This is not difficult but it does impose a certain programming style for the application since some method to monitor the user's actions with the control must be used. One way to monitor the control is to run the application in a scan loop and constantly check for keyboard and mouse input. This is the technique used in this article. If this method is used, the wait statement cannot be used anywhere in the application.
This article walks through the steps and code needed to implement and support the ComboBoxEx in LB. Note: the ComboBoxEx control in this article is also referred to as a combobox. This code is not a complete program so please download the zip file at the end of this page for the source code.
Code
To begin with, the ComboBoxEx requires a COMBOBOXEXITEMA struct to supply information about each item in its dropdown list.
We want this combobox to display both text and images so the mask element of the struct must be set with the value that specifies this.
cbi.mask.struct=7'CBEIF_TEXT or CBEIF_IMAGE or CBEIF_SELECTEDIMAGE
Two more struct elements can be set at this time.
cbi.cchTextMax.struct=100'Limits the list items to 100 characters each.
cbi.iItem.struct=-1'Items will be added to the end of the list.
The next requirement is to load the ComboBoxEx32 class by calling the InitCommonControlsEx API function. Again a small struct is required.
'INITCOMMONCONTROLSEX
Struct iccex, _
dwSize AsUlong,_
dwICC AsUlong'Indicate the size of this struct.
iccex.dwSize.struct= Len(iccex.struct)'Specify the ComboBoxEx32 class.
iccex.dwICC.struct= hexdec("200")'ICC_USEREX_CLASSES.'Load ComboBoxEx32 class.
Calldll #comctl32, "InitCommonControlsEx", iccex As Struct, r AsLong
The combobox has one more requirement. It needs an Image List containing the image or images that will be used. You can create your own bitmap or icons and create an image list for these. This article takes the easy route and uses the Windows system image list. The code download that accompanies this article contains a second program that shows how to create an image list and use your own bitmap for the images.
The combobox in this code will display urls like Internet Explorer and the icon associated with urls is contained in the system image list. All that needs to be done to use this icon is to discover it's position within the image list, and to get the handle of the image list. Windows provides a function called SHGetFileInfo that will do this. Another struct is required for this too.
'SHFILEINFO
Struct shfi, _
hIcon AsUlong, _
iIcon AsLong, _
dwAttributes AsUlong, _
szDisplayName AsChar[260], _
szTypeName AsChar[80]'Get the size of this struct.
lenSHFI = Len(shfi.struct)
SHGetFileInfo will need to know what kind of icon is wanted so the flags must be set for this. The flags are SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX or SHGFI_ICON or SHGFI_SMALLICON and this value is 16657.
With these flags SHGetFileInfo will find the proper icon if you tell it the file extension that you are looking for. In this case it is *.url.
If the function call succeeds the url icon index is returned in shfi.iIcon.struct and the handle to the image list is returned in hSysImgList.
After opening the application window the combobox is created with the CreateWindowExA API function.
hWndParent = hwnd(#main)
Calldll #user32, "GetWindowLongA", hWndParent AsUlong, _
_GWL_HINSTANCE AsLong, hInst AsUlongclass$ ="ComboBoxEx32"
style = _WS_VISIBLE or _WS_CHILD or _WS_VSCROLL or _CBS_AUTOHSCROLL or _CBS_DROPDOWN
Calldll #user32, "CreateWindowExA", 0AsLong, class$ As Ptr, _
""As Ptr, style AsLong, 20AsLong, 20AsLong, _
400AsLong, 150AsLong, hWndParent AsUlong, 0AsLong, _
hInst AsUlong, 0AsLong, hCbe AsUlong'hCbe contains the handle of the new combobox
The next step is to tell the combobox which image list to use by sending a message with the proper command and the handle to the system image list that was obtained earlier.
The last step is to load the combobox with the text to display and the image to go along with it. A different image may be specified for each item in the list. The index of the url icon is used to specify the image in cbi.iImage.struct, and also in cbi.iSelectedImage.struct. This next code loads 10 url strings and sets the image for each one.
CBEM.INSERTITEMA=(_WM_USER+1)For i =1To10'Set the image for this item to the url icon.
cbi.iImage.struct= shfi.iIcon.struct
cbi.iSelectedImage.struct= shfi.iIcon.struct'Set the text.
cbi.pszText.struct="http://www.libertybasic.com/ "+str$(i)'Add the item to the end of the list.
Calldll #user32, "SendMessageA", hCbe AsUlong, _
CBEM.INSERTITEMAAsLong, 0AsLong, cbi As Struct, r AsLongNext i
At this point the combobox has been created and displays the url image for each item in the dropdown list. It would be nice if this was all there was to it but we still need to know when an item is selected from the list and when the user types in a new url and presses the Enter key. A ComboBoxEx is actually a window that contains three controls, a regular combobox along with it's list and edit control. In order to monitor the user's actions with the combobox you need to have the handles to the edit control and the dropdown list. The Windows API provides a function named GetComboBoxInfo that is supposed to give these handles to you. However, in my experience it often fails to return the handle of the edit control when used with an extended combobox. The following sub uses a combination of functions to get the job done.
Sub GetCboExInfo hCboEx32, Byref hwndEdit, Byref hwndList
'In: hCboEx32. The handle of a ComboBoxEx.'Out: The edit control and listbox handles of a ComboBoxEx.
struct cboInfo, cbSize AsLong, pad AsChar[44], hwndList AsLong
cboInfo.cbSize.struct= Len(cboInfo.struct)'Get the handle of the combobox in hCboEx32.
Calldll #user32, "GetWindow", hCboEx32 AsUlong, _GW_CHILD Aslong, hwndCombo AsUlong'Get the handle of the edit control in hwndCombo.
Calldll #user32, "GetWindow", hwndCombo AsUlong, _GW_CHILD AsLong, hwndEdit AsUlong'Get the handle of the listbox in hwndCombo.
Calldll #user32, "GetComboBoxInfo", hwndCombo AsUlong, cboInfo As Struct, r AsLong
hwndList = cboInfo.hwndList.structendsub
Just call this sub and the edit and list handles are returned in hCbeEdit and hCbeList.
'Get the listbox and edit control handles of a ComboBoxEx32.Call GetCboExInfo hCbe, hCbeEdit, hCbeList
Now we have everything needed to use the ComboBoxEx in a Liberty BASIC application except for the code to monitor it for user actions. A pair of subs and a couple of supporting functions handle this quite well. You can throw these away and write your own, but if you look closely at these routines you'll notice that they can be used with a variety of different API created controls with little change. The sub GetEvent is the work horse. It tells you what control the mouse cursor is over when the left mouse button is pressed down, and also when the left mouse button is released after being pressed. It also tells you what control has the focus when the enter key is pressed and also when it's released after being pressed. It's very fast since it only uses one API function to get every virtual keystate in the whole keyboard. Two additional supporting functions are also used, GetMouseFocus() and GetFocus(). To make it easy to add additional keystate information a struct is used for returned values instead of global variables or Byref arguments.
Struct kb,_
hDn AsUlong,_ 'Handle of window or control with focus when a key down occurs, or
_ 'whose client area is under the mouse cursor when a left button
_ 'down occurs.
hUp AsUlong,_ 'Same as above, but when a release occurs.
lBtnDn AsShort,_ 'Left mouse button is down.
lBtnClick AsShort,_ 'Left mouse button has been clicked or pressed and released.
retDown AsShort,_ 'Enter key is down.
retUp AsShort'Enter key has been pressed and released.
Sub GetEvent 'Returns information to kb.struct.'Left mouse: button down, button click, handle of window whose client'area is under the mouse cursor.'Enter key: key down, key up (after key down), handle of window with focus.
keyBuf$ =Space$(256)
Calldll #user32, "GetKeyboardState", keyBuf$ As Ptr, r Aslong'_VK_LBUTTONIfNot(kb.lBtnDn.struct)ThenIfAsc(Mid$(keyBuf$,2))>127Then
kb.lBtnDn.struct=1: kb.hDn.struct= GetMouseFocus()EndIfElseIfAsc(Mid$(keyBuf$,2))<127Then
kb.lBtnClick.struct=1: kb.lBtnDn.struct=0: kb.hUp.struct= GetMouseFocus()EndIfEndIf'_VK_RETURNIfNot(kb.retDown.struct)ThenIfAsc(Mid$(keyBuf$,14))>127Then
kb.retDown.struct=1: kb.hDn.struct= GetFocus()EndIfElseIfAsc(Mid$(keyBuf$,14))<127Then
kb.retUp.struct=1: kb.retDown.struct=0: kb.hUp.struct= GetFocus()EndIfEndIfEndSub
This next sub examines the event information collected by GetEvent and acts upon it.
This sub can also be modified to handle events for different API created controls. Only the events for the combobox are handled in this code.
Sub HandleEvent 'Uses kb.struct for arguments.SelectCaseCase kb.lBtnClick.structIf kb.hDn.struct= hCbeList Then'allow time for the combobox to update it's display.
s = Time$("ms"): While Time$("ms")< s+50: Wend
Print GetWindowText$(hCbe,255)EndIf
kb.lBtnClick.struct=0: kb.hDn.struct=0: kb.hUp.struct=0Case kb.retUp.structIf kb.hUp.struct= hCbeEdit Then
txt$ = GetWindowText$(hCbe,255)If txt$ <>""Then Print "Enter pressed -> ";txt$
EndIf
kb.retUp.struct=0: kb.hDn.struct=0: kb.hUp.struct=0EndSelectEndSub
GetEvent and HandleEvent are the routines to call in your application loop in order to monitor the mouse and keyboard events for the ComboBoxEx.
This next function is used by GetEvent. It's purpose is to return the handle of the window whose client area is under the mouse cursor when it's called.
Function GetMouseFocus()'Returns the handle of the window or control whose'client area is under the mouse cursor.
Struct GMFpoint, x AsLong, y AsLong
Struct GMFrc, left AsLong, top AsLong, right AsLong, bottom AsLong
Calldll #user32, "GetCursorPos", GMFpoint As Struct, ret As Void
X = GMFpoint.x.struct
Y = GMFpoint.y.struct
Calldll #user32, "WindowFromPoint", X AsLong, Y AsLong, hWnd AsUlong
Calldll #user32, "ScreenToClient", hWnd AsUlong, GMFpoint As Struct, ret As Void
x = GMFpoint.x.struct
y = GMFpoint.y.struct
Calldll #user32, "GetClientRect", hWnd AsUlong, GMFrc As Struct, r Aslong
Calldll #user32, "PtInRect", GMFrc As Struct, x AsLong, _
y AsLong, PointOnClient AslongIf PointOnClient Then GetMouseFocus = hWnd
EndFunction
GetFocus(), also used by GetEvent, is just a wrapper for the Win32 function of the same name.
Function GetFocus()
Calldll #user32, "GetFocus", GetFocus AsUlongEndFunction
That pretty well covers it, other than a couple of function wrappers and the code for the window itself. Download the source and try it for yourself.
Back when Internet Explorer 3.0 was released a combobox control became available that has the ability to display images. The aim of this article is to show how to use one of these extended comboboxes, called ComboBoxEx, in a Liberty BASIC application without using a third party DLL, timer or callback.
Since this control is not native to Liberty BASIC it must be created and managed through the Windows API. This is not difficult but it does impose a certain programming style for the application since some method to monitor the user's actions with the control must be used. One way to monitor the control is to run the application in a scan loop and constantly check for keyboard and mouse input. This is the technique used in this article. If this method is used, the wait statement cannot be used anywhere in the application.
This article walks through the steps and code needed to implement and support the ComboBoxEx in LB. Note: the ComboBoxEx control in this article is also referred to as a combobox. This code is not a complete program so please download the zip file at the end of this page for the source code.
Code
To begin with, the ComboBoxEx requires a COMBOBOXEXITEMA struct to supply information about each item in its dropdown list.
We want this combobox to display both text and images so the mask element of the struct must be set with the value that specifies this.
Two more struct elements can be set at this time.
The next requirement is to load the ComboBoxEx32 class by calling the InitCommonControlsEx API function. Again a small struct is required.
The combobox has one more requirement. It needs an Image List containing the image or images that will be used. You can create your own bitmap or icons and create an image list for these. This article takes the easy route and uses the Windows system image list. The code download that accompanies this article contains a second program that shows how to create an image list and use your own bitmap for the images.
The combobox in this code will display urls like Internet Explorer and the icon associated with urls is contained in the system image list. All that needs to be done to use this icon is to discover it's position within the image list, and to get the handle of the image list. Windows provides a function called SHGetFileInfo that will do this. Another struct is required for this too.
SHGetFileInfo will need to know what kind of icon is wanted so the flags must be set for this. The flags are SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX or SHGFI_ICON or SHGFI_SMALLICON and this value is 16657.
With these flags SHGetFileInfo will find the proper icon if you tell it the file extension that you are looking for. In this case it is *.url.
If the function call succeeds the url icon index is returned in shfi.iIcon.struct and the handle to the image list is returned in hSysImgList.
After opening the application window the combobox is created with the CreateWindowExA API function.
The next step is to tell the combobox which image list to use by sending a message with the proper command and the handle to the system image list that was obtained earlier.
The last step is to load the combobox with the text to display and the image to go along with it. A different image may be specified for each item in the list. The index of the url icon is used to specify the image in cbi.iImage.struct, and also in cbi.iSelectedImage.struct. This next code loads 10 url strings and sets the image for each one.
At this point the combobox has been created and displays the url image for each item in the dropdown list. It would be nice if this was all there was to it but we still need to know when an item is selected from the list and when the user types in a new url and presses the Enter key. A ComboBoxEx is actually a window that contains three controls, a regular combobox along with it's list and edit control. In order to monitor the user's actions with the combobox you need to have the handles to the edit control and the dropdown list. The Windows API provides a function named GetComboBoxInfo that is supposed to give these handles to you. However, in my experience it often fails to return the handle of the edit control when used with an extended combobox. The following sub uses a combination of functions to get the job done.
Just call this sub and the edit and list handles are returned in hCbeEdit and hCbeList.
Now we have everything needed to use the ComboBoxEx in a Liberty BASIC application except for the code to monitor it for user actions. A pair of subs and a couple of supporting functions handle this quite well. You can throw these away and write your own, but if you look closely at these routines you'll notice that they can be used with a variety of different API created controls with little change. The sub GetEvent is the work horse. It tells you what control the mouse cursor is over when the left mouse button is pressed down, and also when the left mouse button is released after being pressed. It also tells you what control has the focus when the enter key is pressed and also when it's released after being pressed. It's very fast since it only uses one API function to get every virtual keystate in the whole keyboard. Two additional supporting functions are also used, GetMouseFocus() and GetFocus(). To make it easy to add additional keystate information a struct is used for returned values instead of global variables or Byref arguments.
Struct kb,_ hDn As Ulong,_ 'Handle of window or control with focus when a key down occurs, or _ 'whose client area is under the mouse cursor when a left button _ 'down occurs. hUp As Ulong,_ 'Same as above, but when a release occurs. lBtnDn As Short,_ 'Left mouse button is down. lBtnClick As Short,_ 'Left mouse button has been clicked or pressed and released. retDown As Short,_ 'Enter key is down. retUp As Short 'Enter key has been pressed and released.This next sub examines the event information collected by GetEvent and acts upon it.This sub can also be modified to handle events for different API created controls. Only the events for the combobox are handled in this code.
GetEvent and HandleEvent are the routines to call in your application loop in order to monitor the mouse and keyboard events for the ComboBoxEx.
This next function is used by GetEvent. It's purpose is to return the handle of the window whose client area is under the mouse cursor when it's called.
GetFocus(), also used by GetEvent, is just a wrapper for the Win32 function of the same name.
That pretty well covers it, other than a couple of function wrappers and the code for the window itself. Download the source and try it for yourself.
Source code
For more information on using the ComboBoxEx see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/comboex/comboex.asp or use your favorite search engine and look for "ComboBoxEx Controls".
-