OpenEdge Development: Progress 4GL Handbook
![]() ![]() ![]()
|
Examining the Code the AppBuilder GeneratesIn the previous chapter you didn’t written much code, but rather let the AppBuilder do it for you. This might seem inappropriate in a book about how to learn to program in the Progress 4GL, but there’s a reason for this approach. You need to be familiar with the syntax the AppBuilder generates for you when you put together a window in this way, but you needn’t get into the habit of writing much code like this yourself. The tools are there to do this for you, and you should take advantage of them.
Having said that, in this chapter you’ll look at more of the code the AppBuilder generated for your window, so that you understand how it works. By doing this, you can learn some new 4GL statements used to define visual objects and database queries. But then this chapter explains some of the ways in which this won’t be typical of the complete applications you write. As you progress through the book, you’ll learn about how the OpenEdge development tools can help you create applications where there is no compiled 4GL code for the user interface at all! But to work in that mode, you’ll need to learn a little more about dynamic programming in Progress.
So this chapter provides a grounding in the static definitions that are the starting point for the more dynamic object definitions you’ll use in later chapters. This chapter includes the following sections:
Viewing the entire sample procedure code
In the "Using the Section Editor" , you saw some of the procedural code that gets the window up and running, such as the
enable_UIprocedure, but not the code that defines all the objects in the window. Take a look at that now. As before, since the AppBuilder generates and maintains that code for you based on how you lay out objects in the design window, it doesn’t show the code to you in the Section Editor.To view the entire procedure, you can select either Compile
Code Preview again or File
Print from the AppBuilder’s main menu to print a hard copy of the entire procedure.
Now look through the entire procedure to see some of the syntax that defines the window and its contents. If you look at a printed listing of the procedure file, you’ll notice numerous
ANALYZE-SUSPENDandANALYZE-RESUMEpreprocessors mixed in with the code. The AppBuilder uses these to separate out code it has generated and maintains for you from code you can add to the procedure yourself. These are stripped out of the Code Preview listing for you, making it a little easier to read. Just try to look beyond these things for the time being to learn what the statements are that really define the window.The Definitions section
The top of the procedure file is the Definitions section. This section starts out as an empty template. It provides a space where you can enter documentation information about your procedure. You can access this section by selecting it as the Section type in the Section Editor, as shown in Figure 5–1.
Figure 5–1: The Definitions section
![]()
It’s a good idea to fill this section in and keep it up to date.
There’s just one statement generated for you in this section:
CREATE WIDGET-POOL. Objects that make up an application are sometimes called widgets. Progress uses widget pools to provide memory management for all of the objects that are created in a procedure, including the frames, fields, and buttons in your window as well as some data management objects such as queries. This statement gives you an unnamed widget pool for everything in the procedure. When the procedure finishes executing, all of the memory its objects use is released. This is a good default, but when you get to more advanced programming, you learn how to create named widget pools that have a lifetime you define for them. For the most part you don’t have to worry about them.Below the
CREATE WIDGET-POOLstatement is an area where you can define variables and parameters for your procedure. These variables are scoped to the external procedure. That is, any variables you define here are available for use throughout the procedure file, including any internal procedures it contains. You’ll learn a lot more about scoping in "Procedure Blocks and Data Access."Continuing your look through the Code Preview or printed listing, the next section of the file has all the Preprocessor Definitions the AppBuilder uses to define queries, field lists, and other syntax that can be used in other AppBuilder-generated code throughout the procedure. You’ve seen this before and looked at a few specific definitions in the "Looking at preprocessor values in the Code Preview" .
Window, button, browse, and frame definitions
The next section is labeled Control Definitions. These are definitions for the objects in your window.
Defining a handle variable for the window
First is a definition for the window itself, or rather for a handle used to point to the window’s definition:
There are a couple of things to note about this statement before you examine what a handle really does:
- You can see that the AppBuilder has abbreviated the keyword VARIABLE to VAR. You can abbreviate many Progress 4GL keywords in this way, but you cannot use arbitrary abbreviations. That is, each keyword definition in the language also defines the acceptable abbreviation of the keyword. If you want to confirm the minimum abbreviation for a keyword or find out the kind of statements in which it is used, you can always look it up in the Keyword Index at the back of the third volume of the OpenEdge Development: Progress 4GL Reference. Not all keywords have abbreviations. You should generally not abbreviate any keywords. Typing keywords in full eliminates any chance of a conflict with a future keyword that starts the same way, and generally makes your code more readable. As described in the "Using the Intelligent Edit Control and its shortcuts" , the Edit Control provides aliases for you to eliminate manually typing many of them in full.
- The variable
CustWinis defined as aWIDGET-HANDLE. The term widget applies to all sorts of things, including visual objects such as fields and buttons, as well as other things that can have handles, including queries and even procedures themselves.WIDGET-HANDLEis a synonym for the keywordHANDLE, because a single Progress data type accommodates all these different kinds of objects. Since there is really just oneHANDLEdata type, and since it is used for more than just objects that you might consider widgets, this book always uses the keywordHANDLE.In the "Creating the window" , you’ll look a little more about what the handle does when you get to the code that uses it to create the window.
Defining a button
Next is another kind of
DEFINEstatement. This one defines not a variable, but the first of your buttons:
There is a separate
DEFINEstatement for each different type of object you can have in your application. You can learn all the particular attributes you can set for each kind of object from the OpenEdge Development: Progress 4GL Reference or the online help, but they’re similar in form. TheDEFINEstatement first names the object (BtnFirst in this case) and then has a list of whatever attributes you want to define for the object and their values. In this case the AppBuilder has defined the button’sLABELto be First, because this is what you set it to in the design window. TheSIZEis a standard size the AppBuilder defaults, which you can change by resizing the button in the design window.The following three statements are the definitions for the other three buttons in the window.
Defining a query
After the
DEFINE BUTTONstatement, the next statements define the two queries your procedure uses:
These statements define the query objects that your code later opens using the specific
WHEREclause you defined in the Query Builder. The Order query got its name by default from the browse that displays its data, which you’ll see next. The Customer query got its name from the frame it’s in. You’ll learn more about queries in "Using Queries."Defining a browse
Next is the statement to define your Order browse:
This code first names the query the browse is defined for, and then the list of fields to display, along with their formats. Finally there’s a list of attributes for the browse itself, in a phrase starting with the keyword
WITH. You can set these and other attributes in the Browse property sheet you looked at in the "Using property sheets" .Defining a frame
Next is a section marked Frame Definitions, where the window’s one frame is defined:
Here the code defines the position of each object in the frame. The exact position of the objects depends on how you laid them out in the design window. The four buttons are all at row position 1.48, counting in full character units.
Next come the fields from the Customer table. There are no
DEFINEstatements for these because the field definitions are taken automatically from the Data Dictionary definitions for the Customer fields.Then the frame definition places the browse control at column 13 of row 7.67 .
Finally, the frame definition has its own
WITHclause, where it sets the following frame attributes:
- 1 DOWN — You’ll remember from "Using Basic 4GL Constructs," that the kind of Progress frame you get from a
FOR EACHblock with aDISPLAYstatement in it is a down frame that displays multiple records in a report-like format. Frames in a graphical application are typically one down frames, which display only one instance of the objects defined for the frame. In this case, the browse control is a single GUI object that takes the place of the multi-line down frame in the older interface style that’s designed for character terminals.- NO-BOX, OVERLAY, NO-UNDERLINE, THREE-D — These all define various visual characteristics of the frame and are self-explanatory.
- KEEP-TAB-ORDER — This attribute keeps language statements such as the
ENABLEstatement you saw inenable_UIfrom changing the tab order of the fields.- AT COL 1 ROW 1 — This position is relative to the window the frame is in. The objects in the frame are positioned relative to the frame (their container), and the frame is positioned relative to its container (the window).
- SIZE 86 BY 13.67 — This is the size of the whole frame in characters.
For more information on any of the frame attributes, see their descriptions under the
Frame Phraseentry of the OpenEdge Development: Progress 4GL Reference.Following the Frame Definition section are the Procedure Settings, which are specially formatted comments with information the AppBuilder uses internally. You should never edit special sections like this one.
Creating the window
Now things are going to get a little more interesting. The next part of the generated code, labeled Create Window, has a new type of statement in it that requires a brief explanation of a very basic Progress 4GL concept, that of static versus dynamic objects.
Static objects
Your procedure contains several
DEFINEstatements in it for a variable, a browse, and four buttons. These are called static objects because they are fully defined in the 4GL syntax that names them. Because of this, the Progress compiler knows most everything it needs to know about them when it compiles the procedure. This allows the compiler to store a complete structure in the r-code that defines each static object. At run time the interpreter scans the r-code and builds each object based on its definition.Dynamic Objects
The window is a different kind of object—a dynamic object. You use a
CREATEstatement rather than aDEFINEstatement for it. When you create the object you associate the object with aHANDLEvariable that must already be defined. It is for this that theDEFINE VARIABLE CustWinstatement you saw earlier was coded. The handle acts as a pointer to a structure that describes the object, but it’s different from the structure resulting from aDEFINEstatement in that the structure is only built up at run time, as the program is executing. This allows you to set some or all of the object’s attributes based on program conditions. So, in effect, theCREATEstatement creates an empty shell to be filled in with the object’s description, and the handle points to that shell. Here’s theCREATEstatement for your window:
Around this code is an
IF-THEN-ELSEstatement:
IF SESSION:DISPLAY-TYPE = "GUI":U THENCREATE WINDOW CustWin …ELSE {&WINDOW-NAME} = CURRENT-WINDOW.
This rather cryptic-looking sequence effectively says: “If you’re running in the GUI environment, as opposed to on a character device, then create the new window
CustWin, which will appear as its own identifiable display space on the desktop. Otherwise use the default (and only) window that’s always there for character environments.”This
DISPLAY-TYPEtest is the answer to a question that might have popped into your head:Why has the AppBuilder made the window dynamic when everything else is static?
There is no
DEFINE WINDOWstatement in the 4GL, only theCREATE WINDOWstatement. And this, in turn, is because Progress 4GL procedures are designed to be compilable for different environments largely without change, including graphical and character environments. There’s only one “window” in a character environment, and that is the entire display device. So your code can never ask a character device to create another window. Thus, to have the same code compile and run in both GUI and character, creating the window the GUI environment requires must be conditional. And that is exactly what dynamic objects are for: to let your procedures decide at run time what objects to create and what attributes to give them.To set a dynamic object’s attributes, you normally reference the handle in later program statements. However, in this case the
CREATE WINDOWstatement itself has anASSIGNphrase that sets all the window’s attributes to their proper initial values. In principle, you can define a dynamic object by associating it with a handle, and then set and reset its attributes as needed.Setting attributes of dynamic objects
The next part of the procedure consists of mostly internal comments for the AppBuilder’s benefit, but there’s one executable statement in it, and you’ll look at it to learn one or two more things about dynamic objects:
This statement means: “If the session’s Display Type is GUI and the window handle named
CustWinis valid, then set the window’sHIDDENattribute to no.”The
VALID-HANDLEbuilt-in function tests to see if the structure the handle points to has been properly associated with an object.Will the handle be valid? It should be because the procedure created the window that uses it just above this. But in your own procedures, you can use handles long after they’re defined. You must always make sure that they point to valid structures before you use them.
This language statement demonstrates that in the 4GL you can set the attributes of a dynamic object after the
CREATEstatement by using a reference to the object’s handle, followed by a colon, followed by the attribute name. You can read attributes in the same way, as in:
Using dynamic objects is a very powerful way to write general-purpose procedures that can handle a whole set of variations on any common pattern in your application, whether it is windows with different titles, sizes, and contents, or browses on different queries with different columns displayed. You’ll learn a lot more about these dynamic language constructs in later chapters.
Defining triggers
Skip down to the part of the code marked Control Triggers. Here you’ll find the trigger blocks you defined for the buttons. Before these button triggers, there are a couple of standard AppBuilder-generated triggers to capture window events. Take a look at the second window trigger block:
ON WINDOW-CLOSE OF CustWin /* Customers and Orders */DO:/* This event will close the window and terminate the procedure. */APPLY "CLOSE":U TO THIS-PROCEDURE.RETURN NO-APPLY.END.
Note: The:Utag that follows the quoted string tells the compiler to leave this string out of its list of strings that might be sensible to translate into other human languages. Since the wordCLOSEis just part of the program logic and not something a user would ever see or want to see in a different language, it should never be translated.
WINDOW-CLOSEis one of those events like theCHOOSEevent for a button. Remember that each type of object has its own set of events it can capture.WINDOW-CLOSE, logically enough, is the event that a window receives when it is closed, for example, by choosing the standard close-window box in the corner of the window.The code then cascades this event down to the running procedure itself, by applying the
CLOSEevent to the procedure. There’s a special built-in function that always holds the handle of the currently running procedure:THIS-PROCEDURE. TheAPPLYstatement makes an event happen just as a user action would. The event in this case is theCLOSEevent for the procedure. Keep this in your mind for a few moments, and you’ll soon see what theCLOSEevent does.The
RETURN NO-APPLYstatement just means: “Skip whatever action was in the queue of user interface actions, because we’re getting out anyway.”The button triggers
Next come the four button triggers you defined yourself. Just look at the first of them to confirm the syntax of the
ONstatement:
When you define triggers in the Section Editor, it masks the syntax of the
ONstatement somewhat by putting the event name and the object name into fill-ins that you can select. It also automatically adds theIN FRAMEqualifier for you, based on which frame contains the object.Triggers as event-driven code
There’s one important thing to note about the trigger blocks. In "Introducing the Progress 4GL," you learned that language statements are generally executed or processed in the order in which they appear in the procedure. Now you need to think about the difference between statements being executed and statements merely being processed. Definitional statements such as the
DEFINE VARIABLEandDEFINE BUTTONstatements aren’t executed at all. They just tell Progress to set up the structures they define for later use. The trigger blocks, however, are executable blocks of code, but they aren’t executed when they’re first encountered. They are just set up to be executed when the event they are defined for occurs. You can think of the Progress compiler marching down through the procedure and defining the r-code for each executable statement in sequence. When it encounters a trigger block, it sets aside a special part of the r-code with those statements in it, keyed by the event that triggers the code.This concept is fundamental to the nature of OpenEdge applications. These applications are called event-driven, because to a large extent the procedures in the application just set up blocks of code, and even entire procedures, to be available in memory when user-initiated events call for them.
Looking at the main block
So far most of the code you’ve looked at has been set-up code. It is either definitions of objects and variables or triggers to execute when events happen later on.
Finally you’ve arrived at the part of the procedure that is actually executed when you run the procedure. This is the main block of the procedure. Note that main block is not a Progress 4GL syntax element or a required structure in any way. It is really just a convention observed by the Section Editor. After all the definitions and triggers, you write the executable code for the procedure itself.
The main block of an AppBuilder-generated procedure that creates a window is entirely standard. You can add more initialization code to the block if you wish, but normally you won’t want to remove what it already does. A few of these statements won’t mean a lot to you yet, but for now take a quick look at this block to see a bit more about how events are set up for a typical GUI application.
The first statement simply assigns the value for a built-in function you saw earlier, the
CURRENT-WINDOW. As the comment on the statement explains, this determines defaults for proper parenting of dialog boxes and frames to the windows with which they are associated:
/* Set CURRENT-WINDOW: this will parent dialog-boxes and frames. */ASSIGN CURRENT-WINDOW = {&WINDOW-NAME}THIS-PROCEDURE:CURRENT-WINDOW = {&WINDOW-NAME}.
The next statement is more interesting. Remember that back in the Trigger section there is a trigger on the
WINDOW-CLOSEevent, then another trigger for when the user clicks on the close button in the upper-right corner of a window. This event in turn applies theCLOSEevent to the executing procedure itself:
ON WINDOW-CLOSE OF CustWin /* Customers and Orders */DO:/* This event will close the window and terminate the procedure. */APPLY "CLOSE":U TO THIS-PROCEDURE.RETURN NO-APPLY.END.
Now you have come to the next step in this cascading series of actions:
When the user chooses the close button in the window, a trigger applies a
CLOSEevent to the procedure. Now here in the main block is another trigger block that defines the action for thatCLOSEevent, namely to run an internal procedure calleddisable_UI. This AppBuilder-generated procedure is the counterpart ofenable_UI. It deletes theC-Winstructure that defines the window and then deletes the procedure itself.So now the AppBuilder has set up all the actions necessary to clean up when the window and the procedure that created it are no longer needed.
The next statement,
PAUSE 0 BEFORE-HIDE, sets up a standard GUI default for automatically hiding windows and frames when the user selects some other object that is on top of them.Now you get to the main block itself. The AppBuilder emphasizes this by giving the header name
MAIN-BLOCKto theDO-ENDblock, which is the heart of the procedure:
MAIN-BLOCK:DO ON ERROR UNDO MAIN-BLOCK, LEAVE MAIN-BLOCKON END-KEY UNDO MAIN-BLOCK, LEAVE MAIN-BLOCK:RUN enable_UI.IF NOT THIS-PROCEDURE:PERSISTENT THENWAIT-FOR CLOSE OF THIS-PROCEDURE.END.
The qualifiers on the
DOstatement aren’t important for now. They simply assure that on an error, a cancel, or an escape request from the user, the block terminates. You’ll learn more about undoing blocks in "Managing Transactions."The first action the block takes is to run the internal procedure
enable_UI. You’ve seen thatenable_UIopens the queries, gets a Customer, displays it and its Orders, and enables all the fields. Remember thatenable_UIdoesn’t have to create the window because one of the few executable statements in all the code you looked through before you got to the main block was theCREATE WINDOWstatement. So that has already happened before this point in the code.Don’t worry about the meaning of
THIS-PROCEDURE:PERSISTENTjust yet. This condition isn’t true for this window when you run it by itself, as you’re doing. So Progress executes the statementWAIT-FOR CLOSE OF THIS-PROCEDURE.The
WAIT-FORstatement is the crux of any event-driven application. A standard procedure in an older application, or any procedure that simply performs a task and then returns to its caller, proceeds through its list of statements and then ends. When Progress gets to the final executable statement in a procedure like that, it automatically destroys the procedure (giving back the memory its variables used) and returns to the caller.But in our event-driven procedure, all that really has happened when this last statement is reached is to create a window, set up a few triggers, open two queries, display a record and a browse, and enable some fields. If the procedure were to terminate as soon as that was done, the user would have no chance to interact with the window.
To experiment and see what happens with and without the
WAIT-FORstatement:
- Highlight the two-line statement with the
WAIT-FORkeyword in it:
![]()
- From the AppBuilder menu, select Edit
Format Selection
Comment.
This option puts comment markers around the text you selected. The Comment and Indent options are a handy way to try things out with and without bits of code you’re testing.- Choose the Save button or select File
Save from the menu to save
h-CustOrderWin1.wback to the operating system.Now comes the tricky part. If you simply run the window procedure now from the AppBuilder, it works perfectly well. That is, the window comes up and stays up until the user closes it. How come? The AppBuilder is smart enough to realize that whenever you run any procedure that has a window, it has to persist so that the user can interact with it. So it effectively adds aWAIT-FORstatement for you for testing purposes.To see the actual effect of your change in a run-time environment::
The test window flashes almost imperceptibly and then disappears. All the initialization actions happen but then the procedure terminates. That’s why you need the
WAIT-FOR.To correct this code:
Now the window comes up and stays up, waiting for the events that fire its trigger code, as you choose buttons, and then finally the close event that terminates the procedures and destroys the window.
This exercise illustrates why you have to tell Progress not to terminate the procedure until the user is done with it. This is what the
WAIT-FORstatement does. And when you write aWAIT-FORstatement, you have to tell Progress to wait for some particular event, because otherwise the procedure would never end.So the statement says to wait for the
CLOSEevent on the procedure. And when will this happen? It will be when the user closes the window, as you’ve seen. So at that point the main block ends, theCLOSEtrigger fires, which executes thedisable_UIcode to clean up the resources the procedure and the window use, and everything goes away.The internal procedures
Following the main block you find the code for the two internal procedures the AppBuilder generated,
disable_UIandenable_UI. If you use the Section Editor to define internal procedures, the AppBuilder places them at the end of the source file and keeps them in alphabetical order for you. When the compiler encounters an internal procedure it acts much as it does when it finds a trigger block. It compiles the code and places it in a part of the r-code identified by the procedure name rather than by an event. When Progress encounters aRUNstatement for the internal procedure as it executes, it transfers control to the statements for the internal procedure and then returns to the main procedure when it completes.You can place internal procedure definitions anywhere within the source procedure file. By convention you should place them at the end, but this is strictly an organizational preference. If you use the AppBuilder and its Section Editor to create your procedures, which you should almost always do, then they will organize the code consistently for you.
Contrasting procedural and event-driven programs
Understanding the difference between procedural and event-driven programs is one of the most important requirements of learning how to write OpenEdge applications. The program you have written so far is a simple but good example of a procedural program. It executes basically from the top down. When it comes to a statement to
RUNanother procedure, such as thecalcDaysinternal procedure, then it initiates that procedure, passes parameters into it, and when it gets to the end, returns any output parameters, and that’s the end of it. There’s no code placed off to the side for events initiated by the user. The programmer determines the procedure flow. A program doesn’t prompt the user for input until it’s ready to. The user has little ability to move around in the interface flexibly. This is typical of a character interface application. The simple diagram in Figure 5–2 illustrates top-down or hierarchy programming.Figure 5–2: Procedural top-down application flow
![]()
When the procedure
mainproc.prunsa.p, and thena.prunsa1.p, thenmainproc.p,a.p, anda1.pare in memory. Whena1.preturns, its context is removed and it goes out of scope. Whena.preturns, its context is deleted. And only then can the code runb.p, which might runb1.p. The thread of execution goes right up and down through this hierarchy of called procedures.By contrast, your sample procedure from this chapter is a simple but very good example of an event-driven procedure, as illustrated in Figure 5–3. Most of the code the interpreter encounters as it initializes the procedure doesn’t actually get executed. It just sets up bodies of code to run when the user requests it.
Figure 5–3: Event-driven application flow
![]()
Advantages of the AppBuilder file format
You have seen that the AppBuilder applies a special format to the procedures it generates, regardless of whether they represent a graphical user interface definition or just procedural logic for your application. When you open any procedure created with the AppBuilder, it parses this special format, which includes some specially formatted Progress comments that it alone is expected to read, and identifies all the different sections of the procedure, including object definitions and internal procedures.
Scanning through a long procedure in a listing, or in an editor window, or even in the Code Preview window, doesn’t give you a very good overview of what the procedure does, how it’s put together, and what its contents are. By contrast, when you use the Section Editor, you get a consistent predefined format to your procedures, with only the parts you’re generally interested in shown to you for review or editing. You can get a proper listing of all the elements in the procedure, either all together by choosing the List button or grouped by Section type. You can also print individual sections from the Section Editor. You can print the entire file from the main menu.
The Section Editor provides shortcuts for inserting all manner of text into your procedure, under the Insert menu shown in Figure 5–4.
Figure 5–4: Section Editor Insert menu
![]()
The following options appear under the Insert menu:
- Database Fields — Shows you a list of all tables and fields in your connected databases so you can verify field names you need to insert into a procedure.
- Event Name — Brings up a list of all possible user interface events.
- Procedure Call — Shows you a list of all internal procedures so that you can insert a
RUNstatement for one of them. (Also shown as the Insert Call button in the window.)- Preprocessor Name — Brings up a list of all the preprocessor definitions in the procedure.
- Query — Brings up the Query Builder.
- Object Name — Brings up a list of all fields, variables, and other objects in the procedure.
- File Contents — Lets you insert the contents of an operating system file.
- File Name — Lets you insert the name of a file (for example, in a
RUNstatement).All these options are useful aids that can increase your productivity and accuracy as a programmer.
There’s one more extremely important reason to use the AppBuilder whenever possible. In the example you’ve worked with in this chapter, the AppBuilder generates 4GL code and manages its format in a source file. The AppBuilder can do much more than that. In conjunction with the Progress Dynamics development framework and Progress SmartObjects, the AppBuilder can convert some kinds of procedures into data-driven application components, whose definitions are stored in a repository database and created from that data at run time, eliminating the need for procedural code altogether. This is part of a very advanced concept in OpenEdge development. Keep in mind that any procedure that is readable by the AppBuilder is in a better position to be converted to another useful form automatically, which won’t be the case with hand-written procedures.
Looking ahead
Everything you’ve looked at in these first chapters looks like a pretty easy and powerful way to use the language and the tools to put applications together. And yet there’s a lot more to it than this. In some ways you’re still at the snowplow stage of learning principles that you’ll later apply in very different ways. So what are the essential limitations of the kind of AppBuilder-generated procedure you built in this chapter?
Reusable components
For one thing, the sample procedure is not easily reusable. If you wanted to build a hundred table maintenance windows that all work much the same way (and that’s a basic part of nearly any real business application), you’d have to set up an assembly line process to create them all. In the end you’d wind up with many procedures that create windows that all look and act more or less the same, but they would all be separate procedures requiring separate maintenance and testing. If you needed to make a change to your design, you’d need to make that change to all these separate procedures, and then retest and redeploy them. This would be a major headache and a source of unreliability in your application.
At the same time, you would undoubtedly want to extend the behavior of these windows far beyond what the simple Customers and Orders window does. You might want a real toolbar, for example, with standard icons and a menu. You might want the browse to perform a lot of additional tasks, such as sorting and column reordering and so forth. You might want other kinds of controls in the window, such as drop-down lists of valid values. If you coded each of these additional features for each window, you’d have a tremendous amount of work on your hands.
So OpenEdge provides a set of standard components that do many of these things for you, which you can use to build many different kinds of application windows, as well as the back-end business logic those windows talk to. You can get to know these Progress SmartObjects™ in other OpenEdge documentation.
User interface independence
You might also like to make changes to the interface of your application, and even to change the client platform your application runs on, without having to rewrite your procedures. OpenEdge provides you with the tools to do this as well.
Distributed applications
Another limitation of your sample procedure is that is presumes that the client session running the interface has direct access to the database the application data is coming from. In a modern application, this is normally not the case. Whether you’re running your application on the Web with a browser-based UI, or simply providing global access to your database server from client machines running all over the country or all over the world, you won’t be able to provide all of your users with the same kind of direct database connection that you may have had with older host-based or client/server applications. Later chapters in this book introduce you to how to use the Progress 4GL to construct business logic procedures that can run close to your database server using the OpenEdge AppServer™ technology.
Dynamic programming
And perhaps most remarkable of all, in "Using Graphical Objects in Your Interface," you’ll learn about 4GL constructs that help you build dynamic procedures that can handle whole classes of behavior, so that one procedure can replace dozens or hundreds of older procedures that all did a similar kind of job for you. In this way, you’ll learn how to write more effective and certainly more flexible and maintainable applications by writing far less code than you used to.
So there is a lot of exciting territory ahead. But before you get into some of the more advanced topics, such as how to work with the OpenEdge AppServer and how to write dynamic procedures, there are a lot of important and very powerful basics still to cover. The next chapter, for instance, goes into more depth on some of the block-structured principles that make the Progress 4GL work and how you can use these blocks to retrieve and manage application data. Onward!
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |
![]() ![]() ![]()
|