Ask the Doctor - September 1999

Answers to your Alpha Five Questions

Dr. Wayne,

I need to program a scrolling screen, much like the arrivals/departures screens in airports. Do you have any ideas how I can do that?

Larry

Larry,

This is another place in which the OnTimer event can be used to advantage. We'll create a browse which scrolls down, one record per second. When the last record is reached, the browse will start at the top again. Here it is:

Scrolling browse

Figure 1. Scrolling browse of customers from the sample Invoice.adb

First, create a new form based on customer and place an embedded browse in the middle of the form.

To make the browse scroll, we next need to set a Timer interval for the form. We do this through the form's Properties tab:

Scrolling2

Figure 2. Adjust the Timer interval to at least 1.0 second in the Form designer's Properties.

If you want the user to be able to read the browse, you should not scroll any faster than 1.0 second. The browse in Figure 1 is scrolling at 1 line/second, and that may still be too fast for your application.

Next, we set an OnTimer event for the form. The OnTimer event will be in charge of scrolling through the customer table. The OnTimer event for the form will fire once every Timer interval, or in our case, every second. The OnTimer event is accessed through Form, Actions in form designer mode:

Scrolling3

Figure 3. The OnTimer event script.

The OnTimer event script fetches one record at a time through the customer table. When it reaches the end, it fetches the first record. After each fetch, it resynchs the browse.

Why didn't I just fetch using the form's fetching method? In fact, I started the script in Figure 3 as this.fetch_next(). Using the form's fetch method obviates the need to do a browse1.resynch(). However, I could not find an easy way to test for the last record using form-level methods, so I decided to fetch at the table level and then resynch the form after each fetch.

Start and Stop buttons

I then decided it would be handy to add 2 buttons to stop and restart the scrolling. I can think of an easy way and a not-so-easy way to stop the scrolling.

Let's do the easy way first.

The easy way is to set up a shared-level logical variable, scrolling_allowed, that is set to .t. or .f. by the buttons. Then all we have to do is to modify the OnTimer script to check the value of the variable, as in

dim shared scrolling_allowed as l
if scrolling_allowed=.f. then
 end
end if
dim t as p
t=table.current()
t.fetch_next()
if t.fetch_eof() then
	t.fetch_first()
end if
browse1.refresh()

Script 1. Modified OnTimer event script that checks the value of scrolling_allowed before scrolling.

The OnPush script for the Start scrolling button would then simply be

dim shared scrolling_allowed as l
scrolling_allowed=.t.

Script 2. OnPush script for the Start scrolling button.

The script for the Stop scrolling button is identical, except that scrolling_allowed is assigned to .f..

And now, the harder way.

Just because there's an easy, straightforward way to start and stop the scrolling doesn't mean it's the way that occurred to me first. When I first wrote the code for this article, I chose to directly modify the OnTimer event code through the buttons. Yes, you can change an event script dynamically through Xbasic. You can read and assign the Xbasic for an event with the syntax container.code.eventname. It is easier to show an example than to explain. Here is the code I originally wrote for the Stop scrolling button:

dim shared scrollscript as c
if scrollscript="" then
	scrollscript=parent.code.OnTimer
end if
parent.code.OnTimer=""

Script 3. The original OnPush script for the Stop scrolling button.

The code in script 3 sets the form's OnTimer script to empty, after first saving the original script in a shared variable, scrollscript. To resume scrolling, all we need do is restore the original script:

dim shared scrollscript as c
if scrollscript>"" then
	parent.code.OnTimer=scrollscript
end if

Script 4. The OnPush script for the Start scrolling button that is paired with Script 3.

I thought I was really clever when I wrote Scripts 3 and 4, but let me make it clear that these are absolutely terrible scripts. It's not that they're long or complex -- which they're not -- but try for a moment to imagine that you have to return to these scripts after a couple of years, or even worse, that someone else may one day have to maintain your application. The code in Script 1 is essentially self-documenting: any A5 programmer can look at it and realize that the OnTimer event is checking for the value of a logical variable. All the new programmer has to do is to find where the variable is modified. By contrast, there is no hint at all in the code in Figure 3 that the code will be modified elsewhere. Modifying code through Xbasic may be fun to do, but it is a documentation nightmare. Never, ever, ever create code that changes other code, unless you can't think of any other way to reach your goal. And if you are forced to do so, make sure you put liberal comments in the code so that you or your replacement can figure out what you did!

Dr. Wayne

Have a question about Alpha Five? Send it to DrPWayne@juno.com. Do not send any zipped up data files, but be as specific as you can about your problem.

9/18/99