Home

News

Downloads

Forums

Notes

Readme

Links

Foro en Español


ctl32_balloontip

ctl32_datepicker

ctl32_formstate

ctl32_ftp

ctl32_gripper

ctl32_monthcalendar

ctl32_progressbar

ctl32_scontainer

ctl32_statusbar

ctl32_trackbar


ctl32_statusbar: overview | members | pictures

By Carlos Alloatti

Overview

This class provides a Status Bar control that can be used to replace the native VFP9 Status Bar. If you use a top level form as your program's main form, you can also use this status bar in your top level form.

This class uses the Windows API functions to create a true Windows common controls Status Bar control, with full Themes compliance, and just a couple of class files to add to your project, no DLLs, no FLLs, no OCXs.

This class is a VFPX project.

VFPX: A Visual FoxPro Community effort to create open source add-ons for Visual FoxPro 9.0. The code, classes, and libraries made available here will complement Microsoft's continuing efforts to improve and extend Visual FoxPro with the code named Sedna project which is outlined in the Visual FoxPro Roadmap.


Properties, Events & Methods

You can find a complete reference to all properties, events & methods of the class here.


Main Features

You can use this statusbar to its full potential, using the progressbar, the panels, setting icons, captions, etc. Or you can just drop it into your project and do nothing more than instantiate it at the start of your program, and let it just be a nicer replacement for the native toolbar, or drop one in your main top level form, and enjoy a status bar in your top level form. All texts shown by the menu options and the StatusBarText property of controls will show in the ctl32_statusbar.

So now no more "so you use that FoxPro thing ... is that still around? And why does the Status Bar look so ugly, different from all my other Windows programs? It must be because FoxPro is old..."


Pictures

This is how the native VFP Status Bar looks like. Not very nice for a Windows XP application.

This is how the ctl32_statusbar looks like in a Top Level Form or in the VFP Screen, with all its panels in use.

This is how the ctl32_statusbar looks in Vista Beta 2 Build 5384.


Description of Panels

First Panel (from the left) displays an optional Icon and the normal VFP statusbar messages, the ones normally displayed by the native VFP statusbar. It does not show the record count/record position for open tables, etc. If you set a message at this panel, it overrides all VFP system generated statusbar messages, until you clear the custom message.

Second Panel holds a ProgressBar, a class by itself, can be fully controlled, its width can be adjusted, etc.

Next Panels are the custom Panels (as many as you want in version 3 or higher). These panels can be used to display an icon and a caption. The custom panels can be refered to by name: Panel1, Panel2... or by their index: Panels(1), Panels(2), etc.

The next Panels are the 3 indicator Panels, they display the status of the Insert, CapsLock and NumLock Keys. The text shown is user configurable, and they can be hidden.

Then there is a Date Panel that can display the system date in short or long format, based on the formats and language of the operating system.

Last Panel is not used, is just a divider for the sizing grip area.

All panels except for the first one -the Message Panel- can be shown/hidden.

Here all Panels are hidden.


Quick Start: Using it in a Form

If you want a ctl32_statusbar on a form, drag the ctl32_statusbar control from the Project Manager window and drop it into the Form. Of course there are other ways of doing this. Size it and position it at the bottom of the form. This last step is not required, the control resizes and places itself at the bottom at run time, do it just for aesthetics reasons at design time. Or you can install the builder, by clicking builders\ctl32_statusbar_builder.app Then if you right click in the status bar control in the form designer and choose builder form the shortcut menu, the statusbar will position and size itself.

NOTE: If your form is a Top Level Form, the ctl32_statusbar will use the bottom 24 pixels of your form for itself, so your form will be 24 pixels shorter. If you also have a Top Level Form Menu, thats 20 more pixels that you loose. Check how to layout the controls and use the Anchor property of them.

The only properties you should change at design time are Name and PanelCount.


Quick Start: Using it in the main VFP Screen

Add the following lines to your code:

If Not Pemstatus(_Screen,"StatusBar", 5) Then

   _Screen.Newobject("StatusBar", "ctl32_statusbar", "ctl32.vcx")

Endif

Or, if you want to specify a certain number of custom panels (default is 5):

If Not Pemstatus(_Screen,"StatusBar", 5) Then

   _Screen.Newobject("StatusBar", "ctl32_statusbar", "ctl32.vcx", null, 3)

Endif

To remove the ctl32_statusbar:

_Screen.RemoveObject("StatusBar")

That is all. You dont have to set up anything else if you just want a basic replacement of the native VFP statusbar. The ctl32_statusbar will poll the value of _vfp.StatusBar and the status of Capslock(), NumLock() and Insmode() in a timer event, and update the message.

NOTE: The native VFP statusbar should not be set to OFF or _VFP.StatusBar will not return any value.


ProgressBar Control

There is a ctl32_progressbar control in the ctl32_statusbar, invisible by default. There is a help file for it (in spanish, sorry). Check the properties there, they are mostly self explanatory, and in the sample projects. For a quick start, to see it in action do this:

oStatusBar.ProgressBar.Repeat = .T.

oStatusBar.ProgressBar.Play = .T.

oStatusBar.ProgressBar.Visible = .T.

Just replace oStatusBar with the correct object reference. This progressbar can be used by itself on any form, check the ctl32_progressbar sample project.


Sample Project

See the sample project included in the Sample folder to see examples of how to use the control and what can be done.


BindEvent

This class does Bindevents to the following windows and window messages:

_VFP.hWnd, WM_THEMECHANGED
_Screen, WM_SIZE

If you hook to those same window/event pairs in some other part of your code, recall what the Help file says:

"When binding to Windows message (Win Msg) events, only one hWnd to Windows message pairing can exist."

Icon files used should be included in the project. There is no need to redistribute the icon files to end users. The icon files should contain one 16x16 256 color icon, that is used to display in the statusbar.

Why a Timer? I tried using Bindevent with the _Vfp.StatusBar property, to detect changes to its value and update the text to display, but there is a bug in VFP,  _Vfp.StatusBar changes dont always fire the binded method, so it does not always update correctly. There was no other way but to use a Timer. To read more about this, and a partial workaround, check Calvin Hsia´s Blog, but the problem for the StatusBarText property of controls remains. (And yes, it is a bug.)

If you have a command button in a form that closes the form doing for example Thisform.Release(), and that button has the StatusBarText property set to some value, when the form is closed, the StatusBarText will stay on the statusbar, and kind of hang around to show itself depending on where you point your mouse to.

To solve this, use the following trick:

Procedure cmdClose.Click

   This.StatusBarText = " "

Thisform.Release()

By the way, this is a VFP bug...


Bugs

Version 3 20060522

Andrew Nickless was having a strange problem accesing all the controls in a form in a form method. When his method got to the statusbar, an error was raised, and even trying to get the name property of the object with the problem raised an error.

What I found is that the label and timer control contained in the statusbar somehow got most of their properties set to protected and or hidden. To solve this, cut each object, save the class, open and paste again. Do this with the label and the timer. Really obscure bug, who knows how that happened.

If the status bar is used in a standard Form with ShowWindow = 0 and the form is resized in its Init Event, it seems that the Resize event of the form does not fire, so the status bar does not have a chance to reposition itself. To quick-fix this, add the following code to the ctl32_BindEvents method of the ctl32_statusbar class:

*!* When host is a Standard Form and the form is resized in its Init event,

*!* the Resize event of the form does not fire, so we bind to the form Visible property:

If This.ctl32_FormType = CTL32_FORMTYPE_DEFAULT Then

    Bindevent(Thisform, [Visible], This, [ctl32_Resize], 1)

Endif

Thank you Andrew Nickless for reporting this (VFP) bug.

Version 3 20060510

To prevent the auxiliary toolbar from flashing in slow machines, change the code in the ctl32_statusbar_toolbar Init to this (it just swaps the dock and visible lines)

This.ctl32_DeclareDlls()

*!* Hide the toolbar thru API so its still there but invisible

With This

    *!* 20060519 swaped lines //Doru Constantin//

    .Dock(TOOL_BOTTOM, 0, 0)

    .Visible = TRUE

    ShowWindowAPI(.HWnd, SW_HIDE)

Endwith

A bug in ctl32_ResizePanels has the effect of not showing the message panel caption when all panels are hidden. In ctl32_Resizepanels find this line:

.SetAll([ctl32_Right], -1, [ctl32_StatusBar_Panel])

And change it to this:

.SetAll([ctl32_Right], 0xFFFFFF, [ctl32_StatusBar_Panel])

In ctl32_Width_Access side docked toolbars where not cosidered when calculating the desired width of the status bar in a top level form. Replace ctl32_Width_Access with this code:

*!* RETURN desired Width for StatusBar proxy Window

*!* Width from _Screen is no good, space taken by toolbars on both

*!* sides is subtracted from total Width, so we use _vfp Hwnd

*!* Modified 20060513

Local lnHwnd, lnWidth, lcRect, lnLeft, lnRight

With This

    If .ctl32_FormType = CTL32_FORMTYPE_SCREEN

        m.lnHwnd = _vfp.HWnd

    Else

        m.lnHwnd = Thisform.HWnd

    Endif

    m.lcRect = Space(16)

    GetClientRect(m.lnHwnd, @m.lcRect)

    m.lnLeft = .uStrToLong(Substr(m.lcRect, 1,4))

    *!* m.lnTop = .uStrToLong(Substr(m.lcRect, 5,4))

    m.lnRight = .uStrToLong(Substr(m.lcRect, 9,4))

    *!* m.lnBottom = .uStrToLong(Substr(m.lcRect, 13,4))

    m.lnWidth = m.lnRight - m.lnLeft

Endwith

*!* Width is off by one pixel

m.lnWidth = m.lnWidth + 1

Return m.lnWidth

Problems where reported with screen not updating correctly in certain machines. Solved by commenting the first line of tmrUpdater.Timer:

*!* DoEvents

I must confess I dont even know why that DoEvents was there.

If you are docking programatically a ToolBar at the bottom left of a top level form with a statusbar , you should do this:

oToolbar.Dock(3)

oToolbar.Dock(3)

That way the toobar will not cover the statusbar. There is no problem if the toolbar is docked using the mouse.

Version 2:

When setting the icon property of any panel to an empty string, it will clear the icon of the first panel. To correct this, in the ctl32_statusbar_panel class, icon_assign method, change this line:

SendMessageN(This.Parent.HWnd, SB_SETICON, 0, 0)

To this:

SendMessageN(This.Parent.HWnd, SB_SETICON, This.Index - 1, 0)


Known Problems


Changelog

2006XXXX

Some bugs fixed.

Added ctlSizeGrip.

Added ctlInit that runs after status bar is created, to allow to customize the status bar when using it in a top level form, where it gets created at form activate event.

Fixed sizing grip showing in a maximized _Screen status bar when _Screen.Themes is toggled. nOldWindowState is set to -1.

Fixed sample _Screen status bar click, dblclick and rightclick events not working.

Removed all "With This" from ctl32_statusbar class.

Added code in ctl32_FormActivate to refresh panel captions and icons when using the status bar in a top level form and the captions and icons are set in the control init, see new form ctl32_statusbar_sample_toplevelform2.scx The icons and captions are not displayed without this code, and I don't undertand why.

Removed all "With This" from ctl32_statusbar_panel class. Refactored all assign methods of that class. Added check for control Hwnd <> 0.

Changed the _ThemeStatus, _HostHWnd, _FormType functions in _util of ctl32_common. Eliminated all references to ThisForm, now the form is determined from the control that is passed as a parameter to this functions.

Changed the way _Util object is added to control, from newobject to addproperty, so it will not count as another control.

Added check for assignment of numeric variable to logical property panel.ctlAutosize

Added ctlVcxProgressBar, specifies the name/location of the ctl32_progressbar class.

Added ctlVcxStructs and ctlVcxCommon, they specify the name/location of the ctl32_structs and ctl32_common classes.

Moved and renamed internal use object properties to ctl32_addproperties()

Added ctlUpdatePanels() method and ctlUpdateStyle property

Moved code to determine language to _util class

Added ctlLangId property

Moved ctlos method to _util class

Remove ctlTheme method, using _ThemeStatus of _util

Added width check to prevent panels redrawing when width has not changed.

20060627

  • Reset native VFP container properties to public.
  • Fixed bug where contained object properties got set to hidden/proptected.
  • Added ctl32_structs.vcx class to handle structures in API functions calls.

Under The Hood

When I decided to create a Status Bar control for VFP9, I set myself the following goals:

  • It had to be a direct replacement of the native Status Bar, there should be no need to change anything in a project that uses the native Status Bar to display messages.
  • It should be available in the main screen, or any standard or top level form.
  • There should be no need to do any special setup of the Status Bar to be able to use it, no panels options, or anything else.
  • The child forms should maximize without overlapping the Status Bar, just like the native Status Bar behaves.
  • The appearance of the Status Bar should be like any other standard Windows Status Bar, and follow the theme in use in Windows XP.

Also I wanted to be able to display the current date in the Status Bar, and a progressbar, just like the progressbar shown in the Status Bar of Internet Explorer or Mozilla Firefox.

Some of the following may be outdated now, since I have changed some details in the implementation, but its a good "historical" reference.

Step 1: Decide how to create a standard Status Bar.

The first question was how to create a Status Bar that will follow the standard Windows conventions for a Status Bar. Of course the simplest way is to use the Windows Status Bar control, as described in Status Bars. This control adjusts itself to the bottom (or optionally top) of a parent window, and also adjusts its width to the width of the parent window. Its appearance follows standard Windows conventions.

Step 2: How to get our own Status Bar to display in VFP.

The next step, is to find how to put a Status Bar in a standard Form, in a Top Level Form, and in the main VFP window, and how to make the child forms in the last 2 cases aware of the existence of the Status Bar, so they will not overlap when maximized.

I knew from MSDN that a Windows Status Bar control adjusts its position and width relative to its parent window, so I had to find a way to handle the 3 cases.

a) Status Bar in a standard Form

In a normal form its easy, just use the Form window as the parent window for the Status Bar control, nothing much more to do here, just intercept the Resize event of the Form with a BINDEVENT, and tell the Status Bar Windows control that it has to resize itself and then resize the panels.

The ctl32_resize event of the Ctl32 Status Bar does just that, in the case of a standard VFP Form.

It sends a WM_SIZE message to the Status Bar Windows control,  recalculates the position and size of the panels and sends a  SB_SETPARTS message with the required parameters.

b) Status Bar in a Top Level Form (TLF)

In a Top Level Form, things get a bit more complex, as a Top Level Form in VFP is actually 2 windows:

Here you can see the client window of a VFP Top Level Form highlighted (Winspector). So I started to think how could I manage to put a Status Bar in there and make the child forms aware of it. I decide to explore first how VFP shows its native Status Bar.

c) Status Bar in main VFP window

I started by researching how VFP displays its own Status Bar, to get some clues on how to do it myself.

This is the main VFP window, highlighted in red by Winspector, sized to 800x600 pixels.

 

Here the VFP _screen window is highlighted. The _screen window space is used by child forms.

 

This is the window VFP creates to hold the VFP native Status Bar window and the toolbars windows that are docked at the bottom. Notice the width of the window: 32767 pixels. I presume this window is sized that way so vertically docked toolbars extend just to the top of this window. As the Windows Status Bar control adjusts its width to the parent window, we cannot place it here directly, or its width will be 32767 pixels!

 

Here a toolbar window is highlighted. We know a VFP toolbar window adjusts its width to its contents width, so we cannot use a toolbar to hold our Status Bar control, that adjusts it width to its parent window.

 

Here the VFP Status Bar window is highlighted. The VFP Status Bar adjusts its width to the main VFP window, so maybe we can put our own Windows Status Bar control window in here. At first I tried this, but latter, when trying to put a Status Bar control in a Top Level Form, I found a more convenient way, that worked for both cases, Top Level Forms and VFP main window. What I did is create a proxy window, a window whose only purpose is to server as a container of the statusbar control window.

After seeing what VFP does to display its own Status Bar, I came up with the idea of adding a VFP toolbar to a Top Level Form, and dock it at the bottom of the form, this way VFP itself will create for me a window that I could use.

So I create a simple toolbar with just a shape in it that will result in a 24 pixel high toolbar. I set its ShowWindow property to 1 (In Top Level Form). In the creation stage of the Ctl32 Status Bar, I create an instance of this toolbar and dock it to the bottom of the main VFP window or the Top Level Form as required. Also using API the toolbar window gets hidden. The thing here is that a toolbar cannot be added to a Top Level form until the form is visible, so I Bindevent the Activate event of the form, to run a procedure that adds the toolbar and then unbinds itself.

This is a picture of the status bar in a top level form, with a "cutout" where you can see the underlying toolbar used to create the "space" to hold the status bar.

Using the hwnd property of the toolbar and the GetParent API function, I can get the hwnd of the parent window of the toolbar, This I will use as the parent of the Windows Status Bar control. But wait, this parent window is 32767 pixels wide, so I cannot use it directly, so I create first a static (update: I now create a window using the same class as VFP windows, I "borrow" the window class from VFP) window as a proxy, with a width equal to the width of the Form/VFP window, and use that as a parent for the Windows Status Bar control.

Maybe this list of windows from Winspector can help to get a picture of what is going on:

  • Line 1 in the list is a VFP Top Level Form window.
  • Line 2 is the window created by the toolbar, the one with a width of 32767 pixels. I call it the "Host window"
  • Line 3 is the window I created to hold the Windows Status Bar window. (UPDATE: I now create a window using the same class as VFP windows, I "borrow" the window class from VFP, and I get the fox head icon for free!). I call it the "Proxy window"
  • Line 4 is the ctl32 Status Bar window itself. I call it the "Control window"
  • Line 5 is the ctl32 Progress Bar window
  • Line 6 is the window of the VFP toolbar I add.
  • Line 7 is the client window of the Top Level Form. See the picture of the yellow background Form at the top of this section.

All that is left is do some magic in the Resize event of the Form/VFP window, and all is set. For example, the Windows Status Bar will not show a sizing grip when its parent window is maximized, but in this case its parent window is a static window, so I had to add some code to deal with that.

UPDATE 2006-05-07

I have been strugling to provide support for dealing with _Screen.Themes, Thisform.Themes, and theme switching at the Windows level. It took more time to deal with these issues than to program the whole thing in the first place. I think I finally got it right. You can now set the Themes property of the statusbar, change Thisform.Themes, change _Screen.Themes, or change themes in Windows Display Properties, and everything will look like its supossed to.

I gave up for now on trying to deal with SYS(2700,0) and SYS(2700,1). There is no way to detect the status of that switch, since SYS(2700) returns 1 if a theme is enabled in Windows. I have also seen strange interactions when doing SYS(2700,0/1) and _Screen.Themes .T./.F. many times.

I have also noticed strange interactions between the monitor screen and my eyes after switching themes more than one million times...

I have done some mayor overhauling and moved around things quite a bit, but just the internal workings, so everything is a little more organized and understandable.