Fed up with distributing the 509KB ComCtl32.ocx
with your program just for a single control? In this article, I will
take you through creating the common controls using only the Windows API. I will demonstrate how to create a status bar using the CreateStatusWindowA API
call. Topics such as adding panels, setting text and a font, getting text and positioning
the status bar will be covered.
I first came across this API call in the ComCtrl.h
header file. You can’t find it in the Windows API viewer (don’t know why) but just about
all the calls you need can be found in the Windows Header files.
Note: These files are
written in C++ syntax and therefore you will need to know the basics of translating this
information.
In future articles we will be using the
CreateWindowEx call, but for now we can use the CreateStatusWindow call. When we call this
function, it returns the the window handle (hWnd) of our new control or 0 if it failed. We
can then use this window handle to change the way it looks, and get information about it.
Just a quick note to let you know that all the
project files (a form, bas module, project and compiled exe) amount to a mere 4% of the
ComCtl32.ocx!
Anyway, lets get on. The constants prefixed by WM_ and WS_ are windows messages and windows styles. The constants
prefixed by SB_ are, quite obviously for the status bar. And the constants prefixed by
CCS_ relate to the common controls, in this case, the status bar.
We also use a couple of API calls. These include
the CreateStatusWindow (already mentioned), SendMessage, DestroyWindow and
MoveWindow. The SendMessage, like it sounds, is used for sending messages to our control. The
DestroyWindow clears our control from memory and the MoveWindow is self-explanatory.
All of these API calls and constants can be found
in the modAPI module, found below.
Option Explicit '// NOTE: Some of the constants in this module '// wereextracted from the ComCtrl.h header file '// API calls needed to create our status bar '// through the API '// Main API call used to create the window. '// Returns the window handle of the new '// status bar Public Declare Function CreateStatusWindowA Lib _ "ComCtl32.dll" _ (ByVal style As Long, _ ByVal lpszText As String, _ ByVal hwndParent As Long, ByVal wID As Long) _ As Long '// Used to change/modify styles in our status bar Public Declare Function SendMessage Lib _ "user32" Alias "SendMessageA"_ (ByVal hWnd As Long, _ ByVal wMsg As Long, _ ByVal wParam As Long, lParam As Any) _ As Long '// Used to remove our status bar from memory Public Declare Function DestroyWindow Lib _ "user32" _ (ByVal hWnd As Long) As Long '// Move our status bar so we can see it Public Declare Function MoveWindow Lib _ "user32" (ByVal hWnd As Long, _ ByVal x As Long, ByVal y As Long, ByVal _ nWidth As Long, ByVal nHeight As Long, _ ByVal bRepaint As Long) As Long '// WM_ Windows Messages and Styles Public Const WS_VISIBLE = &H10000000 _ 'Makes it visible Public Const WM_USER = &H400 Public Const WS_CHILD = &H40000000 _ 'Makes it a child of our parent window Public Const WM_SIZE = &H5 _ 'Changes the size Public Const WM_GETFONT = &H31 _ 'Gets the font from a window Public Const WM_SETFONT = &H30 _ 'Sets the font of a window '// Status Bar Messages and Styles Public Const SB_SETTEXT = (WM_USER + 1) _ 'Sets text Public Const sbSimpleIdx = 255 Public Const SBT_SUNKEN = &H0 _ 'Sunken styles Public Const SBT_POPOUT = &H200 _ 'Popped out style Public Const SB_GETTEXTLENGTH = (WM_USER + 3) _ 'Returns the text length Public Const SB_SETPARTS = (WM_USER + 4) _ 'Adds new panels Public Const SB_GETPARTS = (WM_USER + 6) _ 'Retreives panels Public Const SB_GETTEXT = (WM_USER + 2) _ 'Returns text '// Common Control Styles for alignment Public Const CCS_TOP = &H1& 'Top Public Const CCS_BOTTOM = &H3& 'Bottom
Copy and paste that into a new
module (or just use the one in the download).
I
will take you through the functions found in the form’s code. The first,
create (found under the command button cmdCreate) is, quite obviously,
used to create the status bar. When creating it, we need to apply (as a
minimum) the WS_CHILD and WS_VISIBLE constants. These force the status
bar to be visible and make it a child of our form. If you omitted the
WS_CHILD constants the status bar would draw on the desktop. You might
want this, although I doubt your users would! Anyway, after that we use
the MoveWindow API call. By default, the status bar assumes its own
size/position. A call to the MoveWindow function ensures that we can see
our status bar.
The
next part, AddCaption (found under cmdAddCaption) changes the text in
the status bar. I had some problems using this, mainly getting it to
draw text in different panels. You will see what I mean when you use it.
Anyway, this uses a small routine to apply new text. We use the
SendMessage call to apply the new text using the SB_SETTEXT constant. We
also include the style for the panel. I have used the default SBT_SUNKEN
style but you could use the SBT_POPOUT (included) or other styles
supported.
We
now apply a font to the status bar. By using the WM_GETFONT we can get
the font of our form (set to Tahoma) and then use the WM_SETFONT and
SendMessage to apply the font. If you were wrapping this in an activex
control, then you would be getting the font of the usercontrol. You can
get the font of any object that has a hWnd property.
Notice
in the Form_Resize event I have used the MoveWindow call to resize the
status bar. If we didn’t do this, then the status bar would just stay
the same size both in length and width.
The
Add function (found under cmdAdd) basically adds a new panel using the
SB_SETPARTS and the SendMessage. First, we have to define the size of
this panel and then call the SendMessage function. The destroy function
just calls the DestroyWindow with a reference to the status bars window
handle.
The
form requires six command buttons (although this could be scaled down).
Rename them cmdDestroy, cmdCreate, cmdAddCaption, cmdFont, cmdAdd and
cmdGetText. Copy and paste the code below into the form:
Option Explicit '// Holds the window handle for our status bar Private lnghWnd As Long Private Sub cmdCreate_Click() Dim lngStyle As Long 'WS_CHILD - Makes the status bar a child of 'this form. 'WS_VISIBLE - Makes the statusbar visible lngStyle = lngStyle Or WS_CHILD lngStyle = lngStyle Or WS_VISIBLE ' Make the statusbar using the ' CreateStatusWindow API Call ' Note: This is much easier than using ' the CreateWindowEx API lnghWnd = CreateStatusWindowA _ (lngStyle, "VB Square API Drawn _ Statusbar", Me.hWnd, 0) ' By default, our new status bar assumes ' its own position and ' size. Use the MoveWindow API to move it ' to the bottom of the form MoveWindow lnghWnd, 0, ScaleHeight - 20, _ ScaleWidth, 20, True End Sub Private Sub cmdAddCaption_Click() Dim strText As String ' Sets the caption for both panels using a ' string from an input box ' For some reason the text doesn't draw ' If anyone knows why, please let me know strText = InputBox$ _ ("Enter a caption...") SetCaption strText cmdFont_Click End Sub Private Sub cmdFont_Click() Dim lRet As Long ' Set the font of the status bar using ' the WM_SETFONT by getting the font from ' this form using the WM_GETFONT lRet = SendMessage(Me.hWnd, WM_GETFONT, 0, 0) SendMessage lnghWnd, WM_SETFONT, lRet, 0 End Sub Private Sub cmdAdd_Click() Dim lngParts As Long ' Holds the size of the new panel lngParts = ScaleWidth - 100 ' Adds the new panel to our status bar SendMessage lnghWnd, SB_SETPARTS, _ ByVal 2, lngParts End Sub Private Sub cmdDestroy_Click() ' Use the DestroyWindow API to destroy ' our status bar DestroyWindow lnghWnd ' Set the window handle holder to 0 lnghWnd = 0 End Sub Private Sub cmdGetText_Click() Dim pnlNum As Long ' Find out which panel the user wants ' the text from pnlNum = Val _ (InputBox("Text from panel number:")) ' Return the text using the GetText function MsgBox GetText(pnlNum) End Sub Private Sub Form_Load() ' Move the form to the centre of the screen Move (Screen.Width - Width) * 0.5, _ (Screen.Height - Height) * 0.5 ' Set the scalemode to pixels because ' certain functions require this. ScaleMode = vbPixels End Sub Private Sub Form_Resize() ' Use the WM_SIZE constant to resize the ' status bar to keep it at the bottom ' of the form SendMessage lnghWnd, WM_SIZE, 0, 0 End Sub Function GetPanels() As Integer ' Returns the number of panels GetPanels = SendMessage _ (lnghWnd, SB_GETPARTS, 0, ByVal 0) End Function Sub SetCaption(strText As String) Dim lngRet As Long ' Set some new text in the status bar using ' the SM_SETTEXT. Use the SBT_ constants ' to define how the panel looks. lngRet = GetPanels() - 1 SendMessage lnghWnd, SB_SETTEXT, _ ByVal SBT_SUNKEN, ByVal strText SendMessage lnghWnd, SB_SETTEXT, _ ByVal lngRet Or SBT_SUNKEN, ByVal strText End Sub Function GetTextLen(pnlNum As Long) As Long Dim lngRet As Long ' Returns the lenght of the string ' in the panel lngRet = SendMessage _ (lnghWnd, SB_GETTEXTLENGTH, ByVal pnlNum, 0) GetTextLen = (lngRet And &HFFFF&) End Function Function GetText(pnlNum As Long) As String Dim strText As String Dim lngRet As Long ' Returns the string in the panel ' specificed by pnlNum strText = String$(GetTextLen(pnlNum), 0) lngRet = SendMessage(lnghWnd, SB_GETTEXT, _ ByVal pnlNum, ByVal strText) GetText = Left$(strText, _ (lngRet And &HFFFF&)) End Function