# Friday, December 21, 2007

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

 

Friday, December 21, 2007 5:39:25 PM (GMT Standard Time, UTC+00:00)  #    Comments [7]

 

Archive

<December 2007>
SunMonTueWedThuFriSat
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345