Rick Strahl's Weblog
Rick Strahl's FoxPro and Web Connection Weblog
White Papers | Products | Message Board | News |

Creating a generic Timer Evaluation class to handle Refresh issues


1 comment
April 05, 2007 •

The Visual FoxPro UI has a number of little quirks when it comes to properly refreshing itself at times, and in many of my applications I frequently have to futz with the UI to force a proper event loop pickup to occur.

 

This sort of thing is required frequently when dealing with ActiveX components which often don’t refresh properly unless they get Windows events fired to them. Two situations that I see frequently are related to the Microsoft Status Bar control not showing properly on a form or my DataDynamics ActiveBar control not properly popping up its menu popups when invoked via code. In these scenarios the controls usually show up but they are not rendering properly as if they were missing events fired from the Windows message queue.

 

There are number of tricks you can use to get around these issues starting with inserting DoEvents calls and making sure to use the proper combination of _VFP.Autoyield = .T. to force events to fire, but that’s not always reliable.

 

Another common culprit is firing an ActiveX control event from within another ActiveX control event. Even with Autoyield=.T. this often produces unpredictable results as the ActiveX controls don’t get all the events needed to refresh properly usually missing WM_PAINT messages.

 

One common trick I’ve used to get around this issue is to briefly delay execution of problem events by using timers. I stick a timer on a form and enable it to run once and delaying operation by 50 milliseconds or so. This brief interval is usually all it takes for VFP to go into into its UI idle state which is typically enough for the ActiveX controls to catch up on their events before firing the next event at them.

 

Using timers in this way is straight forward – you add a timer on the form and you add the code you want fired in the timer event, then from the code that would normally fire the ActiveX event you set the timer interval instead and off it goes in delayed response.

 

If you do this frequently however, that gets tedious – there’s a lot of setup involved. So rather than doing that I created a utility library that helps me do this generically. The wwEvalTimer class shown below creates a timer and then evaluates a string provided dynamically. It supports a few state properties that can be set with state information to get the proper execution context (important for forms for example). A very simplistic use of the control might look like this:

 

PUBLIC oEval

 

oEval = CREATEOBJECT("wwEvalTimer")

oEval.cEvalExpression = [MESSAGEBOX("Hello " + Time())]

oEval.nIterations = 1

oEval.Interval = 5000

 

 

You basically set the cEvalExpression with any FoxPro code expression. It can be a FoxPro function call or UDF – anything that can execute in an EVAL() basically. In most cases you’ll be pointing it at a UDF or a class method most likely. You can specify an interval which enables the timer. You can also enable iterations so you can loop the timer x number of times firing the eval expression on each iteration.

 

Note the PUBLIC oEval at the top of the code. State is very important as you have to ensure that the timer doesn’t go out of scope if the timer goes out of scope it stops the timer operation so you have to ensure the timer ‘stays’ live either by making it PUBLIC or attaching to an object that sticks around.


State is also important if you need to execute in a specific execution context. For example a common scenario is to execute on a form and fire a form event. Say in Init you want to delay refreshing the ActiveX status bar. In order for the eval expression to have a reference to the form it needs to have some way of being able to reference the form. To serve this need there are several State properties on the object which can be set before execution and then be referenced in the Eval expression.

Here’s a more involved example that executes a timed loop to move the form for a 1000 iterations by calling the form’s Move() method:

 

*** Make sure to use reference that doesn't go out of scope!

THISFORM.ADDPROPERTY("oEvalTimer",CREATEOBJECT("wwEvalTimer"))

 

*** We need to assign the Form to state so we can ref it

THISFORM.oEvalTimer.State = THISFORM

 

*** Now evaluate the expression based on state

THISFORM.oEvalTimer.cEvalExpression = " this.State.Move(this.State.Left + 1)"

 

*** Fire 500 iterations in 10 ms intervals

THISFORM.oEvalTimer.INTERVAL = 10

THISFORM.oEvalTimer.nIterations = 500

 

Note the first is to assign the timer to the form so it stays in scope and doesn’t die as soon as the Init() method completes. Then the state is assigned to THISFORM. The EvalExpression then references this.State which now holds the form reference. And off it goes.

 

Now in a realistic application you’d probably fire off the expression to custom page method. If you need to fire more sophisticated code than an Eval expression or need to create an assignment you’d just create a method on the form and then call that method through the EvalExpression.

 

This routine is very handy and I’ve been using it in a number of different places to remove explicit timers from my application – it’s much cleaner and more logical to follow the code.

 

Here’s the implementation of the wwEvalTimer class:

 

 

*************************************************************

DEFINE CLASS wwEvalTimer AS Timer

*************************************************************

*: Author: Rick Strahl

*:         (c) West Wind Technologies, 2007

*:Contact: http://www.west-wind.com

*:Created: 04/04/2007

*************************************************************

#IF .F.

*:Help Documentation

*:Topic:

Class wwExecuteTimer

 

*:Description:

Class that allows for delayed execution of an evaluation

string.

 

*:Remarks:

 

*:SeeAlso:

 

 

*:ENDHELP

#ENDIF

 

*** A string of to be evaluated by the timer call

*** Call a UDF of      

cEvalExpression = ""

 

*** Sets the number of iterations for this timer to fire

nIterations = 1

  

*** If set cleans up the state variables when execution stops

lCleanupState = .T.

 

*** A state variable that can be set with any value or reference

*** Note that you can assign an object to this state and then

*** reference this State property in the EvaluationExpression

*** for example to fire an event on a Form object you might

*** set the State to THISFORM and then use "this.State.Close()"

*** as an expression

State = .F.

State1 = .F.

State2 = .F.

 

PROTECTED nIterationCount

nIterationCount = 0

 

PROCEDURE TIMER

 

EVALUATE( THIS.cEvalExpression )

 

this.nIterationCount = this.nIterationCount + 1

 

IF (this.nIterationCount >= this.nIterations)

   THIS.INTERVAL = 0

 

   this.nIterationCount = 0

 

   IF THIS.lCleanupState

      this.CleanupState()

   ENDIF

ENDIF

 

ENDFUNC

  

FUNCTION CleanupState()

THIS.State = .f.

this.State1 = .f.

this.State2 = .f.

ENDFUNC  

  

ENDDEFINE

Posted in:

Feedback for this Weblog Entry


Re: Creating a generic Timer Evaluation class to handle Refresh issues



Mike Potjer
April 11, 2007

Cool idea, Rick! Thanks. One of the places where this would have been handy is in a form where I needed to UNBINDEVENTS() in the delegate method, and launch some follow-up code. The unbinding doesn't complete until the delegate method is complete, so I needed to cause the follow-up code to fire outside of the program stack containing the delegate method. I used a timer instance, but your eval timer would have been a cleaner solution.

 
© Rick Strahl, West Wind Technologies, 2003 - 2024