Interesting form paint behavior

A customer sent us a repro scenario of a problem. There was a fairly complicated form with a PageFrame tab control with several pages, each with grids and other controls on it. Sometimes, the form did not paint completely, even though the grid did. It seemed that if you ran the form over a network, it wouldn’t paint, but if you ran it on the local machine, it worked just fine. It turns out to be unrelated to a network, but more related to timing.

Before execution of each line of user code, VFP calls a routine that checks to see if there are any pending events in the Windows event queue to dispatch. For example, the user could have hit <esc> or there could be pending WM_PAINT messages to refresh windows.

That routine doesn’t check for messages each time it’s called: it keeps track of how long it’s been since the last check and only checks if it’s been a while.

That way, if the user is in a tight FOR loop or long SQL Select statement that has no I/O, the <esc> key can interrupt execution, or Alt-Tab back/forth can repaint the windows.

 

When the form is activated. VFP invalidates the entire form rectangle which means Windows will send WM_PAINT messages to repaint it. The user has lots of code that executes when the form gets painted. This will eventually trigger a WM_PAINT message dispatched from that checking routine. Sounds recursive, doesn’t it?!?

The WM_PAINT dispatch forces the rect to be painted by calling a routine that sends a message to all controls to paint themselves. Then ValidateRect is called and VFP thinks the entire form has been painted.

When a control (such as a button on a page or a grid within a grid) is painted, a clipping rectangle is set to the control’s parent container, so that the control doesn’t paint outside its container’s rectangle. If a WM_PAIINT message is dispatched while the clipping rectangle is set, the ValidateRect call says that the entire form has been painted.

A simple way to avoid this behavior is to set _VFP.AutoYield to 0. This will suppress checking and dispatching of windows messages while executing user code.

This code will demonstrate. (Run it a couple times. You might need to tweak the numbers a little). It’s a pageframe with a grid on it. The grid has DynamicBackColor set to call a method that takes a while to execute and alternates boy and girl colors for the rows. The commandbutton and the form background don’t paint correctly: Whatever was behind is now visible. If you force a paint (perhaps by covering/uncovering the window) it paints correctly.

_vfp.AutoYield=1

CREATE CURSOR foo (name c(10))

FOR i = 1 TO 100

      INSERT INTO foo VALUES (TRANSFORM(i))

ENDFOR

LOCATE

PUBLIC x

x=NEWOBJECT("myform")

x.show

ACTIVATE SCREEN

x.show

DEFINE CLASS myform as Form

      allowoutput=.f.

      top=300

      left=200

      PROCEDURE init

            this.AddObject("pf","pageframe")

            this.pf.pagecount=5

            this.pf.top=20

            this.pf.page1.addobject("gr","grid")

            this.pf.page1.gr.column1.DynamicBackColor="thisform.getcolor(RECNO())"

            this.AddObject("cmd","commandbutton")

            this.SetAll("visible",1)

      PROCEDURE getcolor(n)

            ns=SECONDS()+.1

            DO WHILE SECONDS() < ns

            ENDDO

            IF MOD(n,2)=0

                  RETURN 0xffc0c0

            ENDIF

            RETURN 0xc0c0ff

ENDDEFINE