Create a Status Bar using the API
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