Instantiate objects in a container
- Last Updated: December 22, 2023
- 5 minute read
- OpenEdge
- Version 13.0
- Documentation
When you define a frame you give it a name. The AVM creates only one instance of a named frame
within a procedure, so that frame name identifies a unique object. The AVM instantiates
the frame when you first use it by executing an I/O-oriented statement involving the
frame, such as a DISPLAY or VIEW statement. The same
is true for dialog boxes (which are a type of frame), and for menus and their
submenus.
Other objects, however, might not be uniquely identified by their names. When the AVM
encounters, for example, a DEFINE BUTTON statement, or a DEFINE
VARIABLE statement with a VIEW-AS phrase that defines a
particular type of visual object, it registers the description of the object but it does
not actually create it. The AVM can create the object only when you associate it with a
container frame or window that has itself been instantiated. Then the AVM can identify a
unique instance of the object in that container, and create it as part of the
container.
This means that you could create multiple instances of an object in different containers. For example, this code fragment describes a button and creates two instances of it in two different frames:
|
Qualify object references to specify a unique identity
The two ENABLE statements for different frames are two distinct
instances of a single button definition. Each one is said to have a unique identity,
which is manifested in its object handle. Using either the handle or a frame
qualifier that identifies which instance of the object you are referring to, you can
set and retrieve attribute values for the object instances independently of one
another.
Recall that the AVM associates an object reference by default with the most recently defined container for that object. It is important to keep this in mind when you are writing your applications. As with all defaults, you are much better off being explicit about object references than taking your chances with how the defaults work in your case.
Here are a few examples to illustrate this point. If you run the code fragment above
that defines and enables button bBothFrames, it terminates
immediately because there is no statement to make it wait for user input.
To test the effects of how ABL defines a unique identity for an object:
- Add a statement to make the procedure and its frame stay active while you
observe the behavior of the two buttons:
DEFINE BUTTON bBothFrames. ENABLE bBothFrames WITH FRAME F1. ENABLE bBothFrames WITH FRAME F2. WAIT-FOR CLOSE OF THIS-PROCEDURE. - Run this code to see the two instances of the same button, each in its own
frame:
You cannot really see the frames F1 and F2 because the AVM has made them just big enough to hold the buttons. You can see that both buttons have the same default label (the button name), and they are both enabled.
- Add an attribute reference to disable the button:
DEFINE BUTTON bBothFrames. ENABLE bBothFrames WITH FRAME F1. ENABLE bBothFrames WITH FRAME F2. bBothFrames:SENSITIVE = NO. WAIT-FOR CLOSE OF THIS-PROCEDURE.The question is, just which instance of the button are you disabling?
- Run the procedure again to see which button is disabled:
It is the second one, because that is the most recent reference to the button in a frame. To change that behavior (or to make it explicit without relying on the default), you can use the frame qualifier.
- Add this frame phrase to the statement to identify the first frame:
DEFINE BUTTON bBothFrames. ENABLE bBothFrames WITH FRAME F1. ENABLE bBothFrames WITH FRAME F2. bBothFrames:SENSITIVE IN FRAME F1 = NO. WAIT-FOR CLOSE OF THIS-PROCEDURE. - Run the procedure again to see the result:
Now it is the first instance of the button that is disabled.
To make sure it is clear which of these frames is which, you can put the frame name into the button. Make these changes to the procedure:
|
Take a look at the new code you added. The first new statement identifies the button
instance in frame F1 explicitly: bBothFrames:LABEL IN
FRAME F1. It sets the button label attribute to the text
Frame plus the value of the FRAME-NAME
attribute for the button. The second reference to the button in that statement
likewise has to be qualified by the IN FRAME phrase to get the
right one: bBothFrames:FRAME-NAME IN FRAME F1.
By contrast, the second new statement just takes the defaults:
bBothFrames:LABEL = "Frame " + bBothFrames:FRAME-NAME. Because
frame F2 is the most recently referenced frame for the button,
the defaults use that frame. Here is the result when you rerun the procedure.
You can see the frames the AVM created for the buttons. At first they were the same size as the buttons. Your new statements changed the label of each button, and the AVM automatically reduced the button size accordingly. The remaining portion of the frame appears as a white space after each button.
The lesson of this little exercise is that you must always be aware that every use of an object in a different container is a distinct instance of the object, and you should always make your object references as explicit as needed to be sure that you do not simply get a default behavior that is not what you want.