• No results found

Click the Next button

In document 4gl (Page 164-170)

Language statements that define blocks

3. Click the Next button

There is no next Customer, so the Customer record is not changed. The IF AVAILABLE Customer phrase in the Next button trigger assures that nothing happens if there is no Customer to display. However, the preprocessor {&OPEN-BROWSERS-IN-QUERY-CustQuery}, which opens the Order query, isn’t affected by the IF AVAILABLE Customer check, because it’s a separate statement. So the code opens the Order query even if there’s no current Customer, and therefore displays nothing, as shown in Figure 6–4.

Figure 6–4: Example of empty query result

If there’s no Customer you shouldn’t open the Order query, so you need to bring both statements into the code that is executed only when a Customer is available.

Language statements that define blocks

To correct the statement that opens the query:

1. Create another DO-END block in the trigger:

Now the statement that opens the query won’t execute either if there’s no Customer. This is another illustration of how to use the DO block as a way to group multiple statements together, so that they all execute together or not at all. As this example illustrates, you can nest DO blocks as much as you wish.

When you enter this code in the Section Editor, the edit control recognizes the keyword

DO followed by a colon and automatically adds the matching END statement. Just move this statement to where it belongs at the end of the new block. The editor generally matches the indentation of END statements correctly with the start of the block. Make sure you indent all the statements in the block properly so that someone reading your code can easily see how the logic is organized. It’s also a good idea to get into the habit of always putting a comment with each END statement to clarify which block it’s ending. When your code gets complex enough that a single set of nested blocks takes up more than a page, you’ll be grateful you did this. It can prevent all sorts of logic errors.

2. Make this same DO-END correction to the trigger code for the Prev button.

3. Save the window as h-CustOrderWin2.w.

The DO block is considered the most basic kind of block because Progress doesn’t provide any additional services to the block by default. There is no automatic looping within the block, no record reading, and no other special processing done for you behind the scenes. However, you can get a DO block to provide some of these services by adding keywords to the DO statement.

The following section provides some examples.

END. /* END DO IF AVAILABLE Customer */

END.

Looping with a DO block

If you want to loop through a group of statements some specific number of times, use this form of the DO statement:

The following example adds up the integers from one through five and displays the total:

Figure 6–5 shows the result.

Figure 6–5: Result of looping with a DO block

DO variable = expression1 TO expression2

[

BY constant

]

:

DEFINE VARIABLE iCount AS INTEGER NO-UNDO.

DEFINE VARIABLE iTotal AS INTEGER NO-UNDO.

DO iCount = 1 TO 5:

iTotal = iTotal + iCount.

END.

DISPLAY iTotal.

Language statements that define blocks

The starting and ending values can be expressions and not just constants. You can use some value other than one to increment the starting value each time through the loop by using the BY

phrase at the end. If the start and end values are variables or other expressions, Progress evaluates them just once, at the beginning of the first iteration of the block. If the values change inside the block, that doesn’t change how many times the block iterates. For example, the following variation uses the variable that holds the total as the starting expression, after giving it an initial value of one using the INIT (or INITIAL) phrase at the end of the definition:

When you run this procedure, the changes to the variable iTotal inside the loop don’t affect how the loop executes. The final total is one greater than it was before, as shown in Figure 6–6, only because the initial value of the variable is one instead of the default of zero.

Figure 6–6: Example of looping with a DO block with initial value set to 1 If you want to loop through a group of statements for as long as some logical condition is true, you can use this form of the DO block:

DEFINE VARIABLE iCount AS INTEGER NO-UNDO.

DEFINE VARIABLE iTotal AS INTEGER NO-UNDO INIT 1.

DO iCount = iTotal TO 5:

iTotal = iTotal + iCount.

END.

DISPLAY iTotal.

DO WHILE expression:

For the expression, you can use any combination of constants, operators, field names, and variable names that yield a logical (true/false) value. For example:

By its very nature, the DO WHILE statement must evaluate its expression each time through the loop. Otherwise, it would not be able to determine when the condition is no longer true. In this case the variable iTotal, which starts out at one and is doubled until the condition iTotal is less than 50, is no longer true. So what do you expect the final total to be? 32? Not for this code, as shown in Figure 6–7.

Figure 6–7: Example DO WHILE loop result

The reason for this is that Progress evaluates the expression at the beginning of each iteration.

As long as it’s true at that time, the iteration proceeds to the end of the block. At the beginning of the final iteration, iTotal equals 32, so the condition is still true. During that iteration it is doubled one last time to 64. At the beginning of the next iteration the condition 64 < 50 is no longer true, and the block terminates.

When you write a DO WHILE block, make sure that there is always a condition that terminates the block. If the condition is true forever, Progress goes into an infinite loop. If this should happen, press CTRL+BREAK on the keyboard to interrupt Progress so that you can go back and correct your mistake.

DEFINE VARIABLE iTotal AS INTEGER NO-UNDO INIT 1.

DO WHILE iTotal < 50:

iTotal = iTotal * 2.

END.

DISPLAY iTotal.

Language statements that define blocks

Using a DO block to scope records and frames

You’ll learn more about record scoping in Chapter 7, “Record Buffers and Record Scope.” It’s helpful to cover all the syntax forms that can affect it before discussing the meaning of record scope in different situations. For now, you can scope or associate records in one or more tables with a DO block by using the FOR phrase:

Each record names a record you want to work with in the block and scopes it to the block.

References within that block to fields the record contains are automatically associated with that record.

You have already seen an example of frame scoping in the code in the enable_UI procedure of

h-CustOrderWin2.w and in the button triggers you created based on that code:

You can use the phrase WITH FRAME frame-name and, optionally, IN WINDOW window-name to identify which frame you’re talking about when you display fields or take other actions on objects in frames. If you wish, you can scope all the statements in a DO block with a frame by appending the WITH FRAME phrase to the DO statement itself. For example, here’s the BtnNext

trigger block again with the frame qualifier moved to the DO statement:

DO FOR record

[

, record

]

. . .

Whether you name the frame in individual statements or in the block header, this makes sure that Progress understands that you want the fields displayed in the frame where you defined them. If you don’t do this, then depending on the context, Progress might display the fields in a different frame.

To see the result of not explicitly defining a frame scope:

1. Edit the WITH FRAME phrase out of the BtnNext trigger altogether, so it looks like this:

In document 4gl (Page 164-170)