Vista Progress
In addition to the myriad of UI changes that MS has made in Windows Vista, you’ve probably noted that the themed progress bar control (Microsoft Progressbar Control, Version 5.0 SP2) has a slightly slicker appearance. This holds true when the progress bar is presenting information regarding a deterministic process (0 – 100%) and when it is presenting a marquee progress bar for a process that will take an indeterminate amount of time. In the case of the latter the progress bar will float a ghostly green across it’s surface from left to right while the user patiently waits. In any event, the MS Progressbar control appears as an emerald green most of the time (Normal state), but there are times when it can also be ruby red (Error state) or even amber yellow (Paused state).
In Visual FoxPro, the Olecontrol doesn’t offer much control over the Progressbar via properties. I mean sure you can get the plain-jane emerald green and step the progress bar through 1 – 100, but what about those other colors and the marquee mode? Or, what if I wanted the progress bar to appear vertical on my form instead of the default horizontal orientation. I set out to answer the above questions and in the process created a somewhat helpful example for exploring the progress bar control in Windows Vista.
Note: While I am showing and using a few Vista-only features, some of what I am about to show will work with the progress bar in Windows XP as well.
Styles Via SetWindowLong
The style of the progress bar control, including Vertical, Smooth, Smooth Reverse, and Marquee, is set using the progress bar’s HWND, some Window’s constants, and a call to the SetWindowLong API function. An example of this would be:
#Define GWL_STYLE -16
#Define WS_VISIBLE 0x10000000
#Define WS_CHILD 0x40000000
#Define PBS_VERTICAL 0x04
LOCAL lnStyle, lnReturn
Declare Integer SetWindowLong In WIN32API Integer, Integer, Integer
m.lnStyle = Bitor(WS_CHILD, WS_VISIBLE, PBS_VERTICAL)
m.lnReturn = SetWindowLong(This.oleProgress.Object.HWnd, GWL_STYLE, m.lnStyle)
States Via SendMessage
The state of the progress bar control, including Normal, Error, and Paused, is set using the progress bar’s HWND, some Window’s constants, and a call to the SendMessage API function. An example of this would be:
#Define PBST_ERROR 0x0002
#Define PBM_SETSTATE 0x0410
LOCAL lnStyle, lnReturn
Declare Integer SendMessage In user32 Integer, Integer, Integer, Integer
m.lnState = PBST_ERROR
m.lnReturn = SendMessage(This.oleProgress.Object.HWnd, PBM_SETSTATE, m.lnState, 0)
Normal State Progress Bar
This is the way we are used to using the progress bar control in our projects. Simply drop the MS control on a form and go. However, there are a couple of interesting features: Smooth and Smooth Reverse. When these styles are specified for the progress bar window, the progress bar no longer snaps directly to the value. Instead the progress bar displays a smooth transition to the new value. To see this in action set the spinner control on the example form to 0 and then set it to 50 (by typing in the spinner control and tabbing out). The transition from 0 to 50 or vice versa should be quite smooth. Compare this with how the progress bar acts when Smooth and Smooth Reverse are unchecked.
Error State Progress Bar
By using the SendMessage API function and a couple of constants (PBST_ERROR and PBM_SETSTATE), you can easily produce a ruby red, error state progress bar in Windows Vista.
Paused State Progress Bar
Similar to creating the error state progress bar, you can use the SendMessage API function and a couple of constants (PBST_PAUSED and PBM_SETSTATE), you can easily produce an amber yellow, paused state progress bar in Windows Vista.Vertical Progress Bar
Just redimensioning the progress bar so that it is taller than it is wide is not enough to produce a vertical progress bar. However, the PBS_VERTICAL style along with the SetWindowLong API function produce a vertical progress bar quite nicely as the next screen shot attests.
Marquee Progress Bar
If you have a process that is going to take an indeterminate amount of time, you’ll probably want to avail yourself of the marquee style provided by Microsoft’s Progressbar control. Bear in mind that this style is only available when the progressbar is horizontal and requires a timer control to serve as a workaround for a problem in Visual FoxPro with the automatic animation (see comment in Timer1’s timer event).
Progress Bar Example Code
Cut-n-paste the code below into a prg file in Visual FoxPro and execute it.
PUBLIC oform1
oform1=NEWOBJECT(“form1”)
oform1.Show
RETURN
DEFINE CLASS form1 AS form
Top = -2
Left = 0
Height = 355
Width = 353
ShowWindow = 2
DoCreate = .T.
Caption = “Vista Progress Bar Example”
Name = “Form1”
AutoCenter = .T.
ADD OBJECT oleprogress AS olecontrol WITH ;
Top = 25, ;
Left = 26, ;
Height = 25, ;
Width = 300, ;
Name = “oleProgress”, ;
OleClass = “COMCTL.ProgCtrl.1”
ADD OBJECT timer1 AS timer WITH ;
Top = 60, ;
Left = 168, ;
Height = 23, ;
Width = 23, ;
Name = “Timer1”
ADD OBJECT opgstate AS optiongroup WITH ;
AutoSize = .T., ;
ButtonCount = 3, ;
BackStyle = 0, ;
Value = 1, ;
Height = 65, ;
Left = 212, ;
Top = 150, ;
Width = 71, ;
Name = “opgState”, ;
Option1.BackStyle = 0, ;
Option1.Caption = “Normal”, ;
Option1.Value = 1, ;
Option1.Height = 17, ;
Option1.Left = 5, ;
Option1.Top = 5, ;
Option1.Width = 59, ;
Option1.AutoSize = .T., ;
Option1.Name = “Option1”, ;
Option2.BackStyle = 0, ;
Option2.Caption = “Error”, ;
Option2.Height = 17, ;
Option2.Left = 5, ;
Option2.Top = 24, ;
Option2.Width = 45, ;
Option2.AutoSize = .T., ;
Option2.Name = “Option2”, ;
Option3.BackStyle = 0, ;
Option3.Caption = “Paused”, ;
Option3.Height = 17, ;
Option3.Left = 5, ;
Option3.Top = 43, ;
Option3.Width = 61, ;
Option3.AutoSize = .T., ;
Option3.Name = “Option3”
ADD OBJECT spnprogress AS spinner WITH ;
Height = 24, ;
KeyboardHighValue = 100, ;
KeyboardLowValue = 0, ;
Left = 150, ;
SpinnerHighValue = 100.00, ;
SpinnerLowValue = 0.00, ;
Top = 115, ;
Width = 72, ;
Format = “999”, ;
Name = “spnProgress”
ADD OBJECT chkvertical AS checkbox WITH ;
Top = 150, ;
Left = 92, ;
Height = 17, ;
Width = 58, ;
AutoSize = .T., ;
Alignment = 0, ;
BackStyle = 0, ;
Caption = “Vertical”, ;
Value = .F., ;
Name = “chkVertical”
ADD OBJECT chksmooth AS checkbox WITH ;
Top = 174, ;
Left = 92, ;
Height = 17, ;
Width = 61, ;
AutoSize = .T., ;
Alignment = 0, ;
BackStyle = 0, ;
Caption = “Smooth”, ;
Value = .F., ;
Name = “chkSmooth”
ADD OBJECT chksmoothreverse AS checkbox WITH ;
Top = 198, ;
Left = 92, ;
Height = 17, ;
Width = 110, ;
AutoSize = .T., ;
Alignment = 0, ;
BackStyle = 0, ;
Caption = “Smooth Reverse”, ;
Value = .F., ;
Name = “chkSmoothReverse”
ADD OBJECT chkmarquee AS checkbox WITH ;
Top = 222, ;
Left = 92, ;
Height = 17, ;
Width = 66, ;
AutoSize = .T., ;
Alignment = 0, ;
BackStyle = 0, ;
Caption = “Marquee”, ;
Value = .F., ;
Name = “chkMarquee”
ADD OBJECT label1 AS label WITH ;
AutoSize = .T., ;
BackStyle = 0, ;
Caption = “Progress”, ;
Height = 17, ;
Left = 92, ;
Top = 119, ;
Width = 53, ;
Name = “Label1”
ADD OBJECT label2 AS label WITH ;
AutoSize = .T., ;
BackStyle = 0, ;
Caption = “%”, ;
Height = 17, ;
Left = 228, ;
Top = 119, ;
Width = 13, ;
Name = “Label2”
ADD OBJECT label3 AS label WITH ;
AutoSize = .T., ;
BackStyle = 0, ;
Caption = “Microsoft ProgressBar Control, version 5.0 (SP2)”, ;
Height = 17, ;
Left = 26, ;
Top = 6, ;
Width = 267, ;
Name = “Label3”
PROCEDURE setprogressbar
#Define GWL_STYLE -16
#Define WM_USER 0x0400
#Define WS_VISIBLE 0x10000000
#Define WS_CHILD 0x40000000
#Define PBM_SETRANGE 0x0401
#Define PBM_SETPOS 0x0402
#Define PBM_DELTAPOS 0x0403
#Define PBM_SETSTEP 0x0404
#Define PBM_STEPIT 0x0405
#Define PBM_SETRANGE32 0x0406
#Define PBM_GETRANGE 0x0407
#Define PBM_GETPOS 0x0408
#Define PBM_SETBARCOLOR 0x0409
#Define PBM_SETBKCOLOR 0x2001
#Define PBM_SETMARQUEE 0x040A
#Define PBM_GETSTEP 0x040D
#Define PBM_GETBKCOLOR 0x040E
#Define PBM_GETBARCOLOR 0x040F
#Define PBM_SETSTATE 0x0410
#Define PBM_GETSTATE 0x0411
#Define PBS_SMOOTH 0x01
#Define PBS_VERTICAL 0x04
#Define PBS_MARQUEE 0x08
#Define PBS_SMOOTHREVERSE 0x10
#Define PBST_NORMAL 0x0001
#Define PBST_ERROR 0x0002
#Define PBST_PAUSED 0x0003
*!* Style = GetWindowLong (Thisform.oleProgress.Object.HWnd, GWL_STYLE)
LOCAL lnStyle, lnReturn, lnState
Declare Integer SendMessage In user32 Integer, Integer, Integer, Integer
Declare Integer SetWindowLong In WIN32API Integer, Integer, Integer
Declare Integer GetWindowLong In user32 Integer, Integer
this.timer1.Interval = 0
this.oleProgress.Value = 0
this.spnProgress.Value = 0
m.lnStyle = Bitor(WS_CHILD, WS_VISIBLE)
IF this.chkVertical.Value
m.lnStyle = Bitor(m.lnStyle, PBS_VERTICAL)
this.oleProgress.Width = 25
this.oleProgress.height = 300
ELSE
this.oleProgress.height = 25
this.oleProgress.width = 300
ENDIF
IF this.chkSmooth.Value
m.lnStyle = Bitor(m.lnStyle, PBS_SMOOTH)
ENDIF
IF this.chkSmoothReverse.Value
m.lnStyle = Bitor(m.lnStyle, PBS_SMOOTHREVERSE)
ENDIF
IF this.chkMarquee.Value
m.lnStyle = Bitor(m.lnStyle, PBS_MARQUEE)
ENDIF
m.lnReturn = SetWindowLong(This.oleProgress.Object.HWnd, GWL_STYLE, m.lnStyle)
DO case
CASE this.opgState.Value = 1
m.lnState = PBST_NORMAL
CASE this.opgState.Value = 2
m.lnState = PBST_ERROR
CASE this.opgState.Value = 3
m.lnState = PBST_PAUSED
ENDCASE
m.lnReturn = SendMessage(This.oleProgress.Object.HWnd, PBM_SETSTATE, m.lnState, 0)
IF this.chkMarquee.Value
this.timer1.Interval = 100
ENDIF
ENDPROC
PROCEDURE timer1.Timer
*!* Work around as SendMessage does not work
*!* to set the animation to automatic using:
*!* m.lnReturn = SendMessage(Thisform.oleProgress.Object.HWnd, PBM_SETMARQUEE, 1, 100)
thisform.oleProgress.object.value = 0
ENDPROC
PROCEDURE opgstate.InteractiveChange
thisform.setprogressbar()
ENDPROC
PROCEDURE spnprogress.InteractiveChange
thisform.oleProgress.value = this.Value
ENDPROC
PROCEDURE spnprogress.Valid
this.interactivechange()
ENDPROC
PROCEDURE chkvertical.Valid
thisform.setprogressbar()
ENDPROC
PROCEDURE chksmooth.Valid
thisform.setprogressbar()
ENDPROC
PROCEDURE chksmoothreverse.Valid
thisform.setprogressbar()
ENDPROC
PROCEDURE chkmarquee.Valid
LOCAL llNotValue
m.llNotValue = !this.Value
thisform.spnProgress.Enabled = m.llNotValue
thisform.chkVertical.enabled = m.llNotValue
thisform.chkSmooth.enabled = m.llNotValue
thisform.chkSmoothReverse.enabled = m.llNotValue
thisform.opgState.option2.Enabled = m.llNotValue
thisform.opgState.option3.Enabled = m.llNotValue
IF !m.llNotValue
thisform.chkVertical.value = .F.
thisform.chkSmooth.value = .F.
thisform.chkSmoothReverse.value = .F.
thisform.opgState.Value = 1
thisform.Refresh()
ENDIF
thisform.setprogressbar()
ENDPROC
ENDDEFINE