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 0x04LOCAL lnStyle, lnReturnDeclare Integer SetWindowLong In WIN32API Integer, Integer, Integerm.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 0x0410LOCAL lnStyle, lnReturnDeclare Integer SendMessage In user32 Integer, Integer, Integer, Integerm.lnState = PBST_ERRORm.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
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 oform1oform1=NEWOBJECT("form1")oform1.ShowRETURNDEFINE 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() ENDPROCENDDEFINE
Remember Me
a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u