One of the basic tasks of the Windows programmer is to write dialog boxes. This chapter
will show how to develop dialog boxes with APL+Win.
In order to learn the basics, we will develop a very simple dialog box which looks like
this:
Figure 1 - The dialog box to develop
Using the ]WED forms editor
The simplest way to create a dialog box is to use the ]WED editor.
Simply enter a command such as:
]WED fmDb
where fmDb will be the name of your new dialog box
form.
The following windows are displayed on the screen. The top window is
the pilot window from which you can select an object (left combo box), select a property
or event for this object (middle combo box) and enter a new value for this property or
event (right combo box).
Figure 2 - The ]WED forms editor windows
The bottom left window is the objects palette from which you can pick
objects to install on your dialog box.
The bottom right window is the dialog box we are going to design.
Perform the following steps:
Resizing the form
- using the left mouse button, resize the fmDb form to a smaller
form
Changing form properties
- select the caption property from the Property
combo box
- type: My first dialog box in the Value field and
press Enter
- select the scale property from the Property combo
box
- replace the content of the Value field by: 5 (5
means pixel coordinates) and press Enter
Adding a control on the form
- click on the OK button in the objects palette
- using the right mouse button, drag it to the right
Moving a control on the form
- click on the bn1 button you just created and move it to the top
right corner of the form
Your form should now look more or less like the following:

Figure 3 - Dialog box in design mode
Adding a second button of the same size as the first one
- click on the bn1 button to select it
- select where from the Property combo box
- highlight the last 3 values of the where property in the Value
field and press Ctrl+C (or Ctrl+Ins)
(this copies them to the clipboard)
- click on the OK button in the objects palette
- using the right mouse button on the dialog box, drag towards
right and bottom to give the button a size
- click on the bn2 button to select it
- select where from the Property combo box
- in the Value field, highlight the last 3 values and
press Ctrl+V (or Shift+Ins) and then Enter
(this forces button bn2 size to be the same horizontal position and size as
button bn1)
As you see, you can resize an object with the mouse, by dragging one
of its handles, or by changing its where property.
Changing buttons properties
- click on button bn1 to select it
- click in the Property combo and press C
- in the Value field, type: OK and press Enter
- click on button bn2 to select it
(note that the current property stays set to caption)
- in the Value field, type: Cancel and press Enter
Your form should like the following:

Figure 4 - Dialog box in design mode (continued)
Adding more controls
- click on the tools palette object which displays LABEL
- on the form, towards the bottom, drag to the right and bottom to give
a size to your label
- select the caption property and type: Question then
press Enter
- select the where property and change the next to last value to:
16 and press Enter
- click on the tools palette object which displays EDIT
- on the form, towards the bottom, drag to the right and bottom to give
a size to your edit control
- select the where property and change the next to last value to:
20 and press Enter
The dialog box should look like the following:

Figure 5 - Dialog box in design mode (continued)
If you could obtain a form similar to the above one, congratulations,
you did it right.
Saving your work
At any time while working with the ]WED forms editor you can press Ctrl+S to save your work.
When you press Ctrl+S, an image of your form, as defined so
far, is saved in a variable called formname_def where formname
is your current form name (here: fmDB).
Another way to save your work is to press Ctrl+D: this saves
your form definition as an APL function, called formname_Make where formname
is your current form name (here: fmDB). You can also press Ctrl+E at any
time to exit after saving your form's definition as an variable called formname_def.
Note that when pressing Ctrl+S, Ctrl+D or Ctrl+E
you are saving your work in the active workspace: you still need to save your active
workspace!
- press Ctrl+D now, then press Ctrl+E.
This should have created variable fmDb_def and function fmDB_Make in
your APL workspace.
Here is what the fmDb_def nested array looks like:
fmDb_def
710430785 66055 19961016 132011390 OfmDb .717986918E10 CF
orm 768 Pscale 5 0 0 193 305 Pcaption My first Dialo
g Box C_Window 512 Pextent 12.0625 38.125 Pwhere 11
21.875 Obn1 0 Cbutton 1024 Pcaption OK C_Window 512
Pwhere 10 220 25 75 Obn2 0 CButton 1024 Pcaption Can
cel C_Window 512 Pwhere 40 220 25 75 Ol1 0 CLabel 25
60 Pcaption Question C_Window 512 Pwhere 145 5 15 290
Oed1 0 CEdit 1792 C_Window 512 Pwhere 165 5 20 290
˝fmDb_def
56
fmDb_def[9 10]
Pscale 5 0 0 193 305
Do not try to manually change this nested array: you would not be
able to reload the form in the ]WED editor from it later! That is why I think it is better
to work with the formname_Make functions and to save your work with Ctrl+D followed
by Ctrl+E.
Here is what the fmDb_Make function looks like:
’ fmDb_Make;x;Śwself
[1] ©'fmDb_Make - Created 10/16/96 at 14:24:24
[2] 'fmDb' Świ 'Delete'
[3]
[4] Śwself„'fmDb' Świ 'New' 'Form' 'Close'
[5] Świ 'scale' 5 0 0 193 305
[6] Świ 'caption' 'My first Dialog Box'
[7] Świ 'extent' 12.0625 38.125
[8] Świ 'where' 11 21.875
[9]
[10] Śwself„'fmDb.bn1' Świ 'New' 'Button'
[11] Świ 'caption' 'OK'
[12] Świ 'where' 10 220 25 75
[13]
[14] Śwself„'fmDb.bn2' Świ 'New' 'Button'
[15] Świ 'caption' 'Cancel'
[16] Świ 'where' 40 220 25 75
[17]
[18] Śwself„'fmDb.l1' Świ 'New' 'Label'
[19] Świ 'caption' 'Question'
[20] Świ 'where' 145 5 15 290
[21]
[22] Śwself„'fmDb.ed1' Świ 'New' 'Edit'
[23] Świ 'where' 165 5 20 290
’
At any time you may run this fmDb_Make function to recreate a form
identical to the one you designed with ].
Handling events
Unlike DOS programming which used to be " procedural",
Windows programming is " events programming".
Events programming consists in associating APL functions (or APL
expressions) to events that occur in your form. Such functions are called "
event-handler functions" or " callback functions".
Events are special object properties which names are prefixed by
" on". Example: onClick denotes a mouse click event occurring on
an object. Events are listed in the Property combo box of the ]WED main
form.
In order to make our form a useful form, we must return the text
entered by the user in the edit control and we need to do this when the user clicks the OK
button.
We also need to close the form when the user clicks the OK
button.
So, let us start again the ]WED editor:
]WED fmDb
When you do so, your fmDb form is displayed in its last saved state.
- click on the OK button to select it
- click on the Property combo box and select onClick
- click on the [...]button to the right of the Value
field
(this opens an edit window with the fmDb_bn1_Click function name already typed in)
You can start writing your fmDb_bn1_Click callback function right
there, without quitting the ]WED editor.

Figure 6 - Edit session for function fmDb_bn1_Click
The fmDb_bn1_Click callback function will be executed whenever a
click occurs on the OK button.
The first instruction queries the text property of the
fmDB.ed1 edit control (i.e. the text entered by the user in the edit control) and saves it
in variable A.
The second instruction sets the value property of the fmDB
form to be this exact text. We need this because a form returns its value property
as a result of its Wait method when it is closed.
The third line asks the form to invoke its Close method.
As this short example shows:
- object names are named according to a parent-child relationship
denoted with the . symbol
- objects have properties (like text, value, ...) that can
be queried or set with the WI system function. In APL+Win properties always start
with a lowercase letter and are entirely lowercase. Case is significant.
- objects have methods (like Close, ...) which are built in
object behavior which can be invoked using the WI
system function (methods are very
similar to programs which are run to perform a certain task on the object). Methods always
start with an uppercase letter, all other letters being lowercase. Case is significant.
- press Ctrl+D, confirm updating the fmDb_Make function and press
Ctrl+E
Tip: do not forget to save your workspace or save the
fmDb_Make and fmDb_bn1_Click functions to your UCMD (User Command) library file.
Running a dialog box
We now need to run our brand new Windows fmDb application. This is
simply done by asking the fmDb form to run its Wait method:
'fmDb'Świ'Wait'
Our form immediately displays itself on the screen.

Figure 7 - Dialog box in run mode
- click in the edit control field, type APL+Win and click the OK
button.
As soon as the form closes, the 'fmDb'wi'Wait' instruction
terminates and returns the edit control content (APL+Win).
Naming objects
It is good practice, in order to make programs more readable and
maintainable, to use meaningful objects names.
For example, instead of using the default fmDb.bn1 and fmDb.bn2
button names, it would be much better to use fmDb.bnOK and fmDb.bnCancel.
It is also recommended APL+Win practice to prefix objects with a few
characters which help identify their class (example: fm for form, bn for
button, l for label, ed for edit control, etc.). We are doing that
systematically here.
Similarly, it would be better to use fmDb.lQst and fmDb.edQst than
fmDb.l1 and fmDb.ed1, especially if there were other labels and edit controls on the form.
We should have renamed these objects immediately after their
creation.
Let us rename all of our dialog box objects. Pay attention, this also
means changing our fmDb_bn1_Click callback function accordingly.
Enter the form's editor once again:
]WED fmDb
- select an object by clicking on it
- click name in the Property combo box
- change its name in the Value field and press Enter
- repeat these steps for each object
(rename the objects as described above)
- select the OK button by clicking on it
- select the onClick event from the Property combo
box
- click on the [...]button to the right of the Value
field
- change the fmDb_bn1_Click so that it now reads:
’ fmDb_bnOK_Click;A
[1] A„'fmDb.edQst'Świ'text'
[2] 'fmDb'Świ'value' A
[3] 0 0˝'fmDb'Świ'Close'
’
- press Ctrl+E to save the new function definition
- change fmDb_bn1_Click to fmDb_bnOK_Click in the Value
field
Note Renaming an object for which the caption property
has been set different from its name property, automatically changes the caption
property: therefore you will have to change the label's caption back to Question
after you rename the label to lQst.
Tip as you see, DO NOT WAIT for renaming objects with
meaningful names: do it right at the time you create them, to avoid having to makes
changes in callback functions.
Handling more events
As it stands, we already have a functioning Windows application! And
we have had very little code to write: just 3 lines of easy APL code:
’ fmDb_bnOK_Click;A
[1] A„'fmDb.edQst'Świ'text'
[2] 'fmDb'Świ'value' A
[3] 0 0˝'fmDb'Świ'Close'
’
Our application knows how to behave in case of a Click on the OK
button, but we have not yet taught it to behave in case of Click on the Cancel
button.
In case of Click on the Cancel button, it would be a
good idea to return a value that the user could not type in the edit control, so that we
can easily test the way our form was closed. I propose to return an empty integer matrix:
[0 0˝0 when the user clicks the Cancel button.
- click on the Cancel button to select it
- select onClick in the Property combo box
- click on the [...]button to the right of the Value
field
(this opens an edit window with the fmDb_bnCancel_Click function name already typed in for
you)
Define your fmDb_bnCancel_Click callback function as follows:

Figure 8 - Edit session for function fmDb_bnCancel_Click
The fmDb_bnCancel_Click callback function will be executed whenever a
click occurs on the Cancel button.
- press Ctrl+D, confirm updating the fmDb_Make function and press
Ctrl+E
Tip Do not forget to save your workspace or save the fmDb_Make
and fmDb_bn1_Click functions to your UCMD (User Command) library file.
Testing the dialog box
We can once again test our dialog box to check our Cancel
button behavior.
Remember to rerun function fmDb_Make before running your form:
fmDb_Make
(0 0˝0)'fmDb' Świ 'Wait'

Figure 9 - Testing the Cancel button
- press Cancel
Tip It is necessary to rerun function fmDb_Make before running
the form, because we have made several changes using the ]WED editor and in particular
added a callback to the Cancel button; without rerunning the fmDb_Make function, we
would use the old version of the form which did not know about this callback):
Improving our application
You may think that our Windows application is finished. However, it
may be significantly improved before it can become a real utility Windows application.
One thing you may have noticed is that we must click within the edit
control before we can type in it: this is because this control is not given the focus by
default when the application starts.
Giving the focus to the edit control at startup
How can we force the focus to go to the edit control when the form
starts up?
Knowing that edit control objects have a Focus method, you
might suggest that we simply invoke this method on the fmDb.edQst object just before
calling our application. Let's try it:
'fmDb.edQst' Świ 'Focus'
ŚWI FOCUS ERROR: Window not open
'fmDb.edQst' Świ 'Focus'
^
This is unfortunately not possible, because we cannot invoke methods
on closed objects.
The solution is (as it is almost ALWAYS the case) to run this method
in a callback function.
The question you will ask, then, is: a callback function for which
event on which object?
That's a good question. More generally, the question, when you need
to perform a task which does not seem directly related to an event occurring in your
application:
which event should I use to perform the Windows task I
need to perform?
You must know that, when a form is started, a number of events are
automatically generated. These events are:
Open, Resize, Show, Focus, Paint, Wait
The general answer to the above general question is:
you must perform a Windows task in the event which
most closely relates to the task
For example, if you need to draw a picture on your form, you must do
it in the onPaint event handler of your form. If you need to resize an object on
your form, you need to do it in the onResize event handler of your form.
Here we need to force the focus to go to the fmDb.edQst object when
the form is started, so we need to do that in the onFocus event handler of the
form.
Start the ]WED editor again:
]WED fmDb
then:
- click on the form outside any object
(fmDb must appear in the Object field in the top window)
- select onFocus from the Property combo box
- click on the [...]button to the right of the Value
field
- define your event handler as follows:

Figure 10 - Edit session for function fmDb_Focus
- press Ctrl+D, confirm updating the fmDb_Make function and press
Ctrl+E
Tip Do not forget to save your workspace or save the fmDb_Make
and fmDb_bn1_Click functions to your UCMD (User Command) library file.
Let's test our form again:
fmDb_Make
'fmDb' Świ 'Wait'

Figure 11 - Testing the focus setting to the edit control
As the above form shows the caret is now displayed in the edit
control as soon as the form shows up and you can start typing immediately in the edit
control.
Changing the form border
A dialog box is a form which is normally not resizeable. It should
also have a modal border.
This is achieved by changing the form's border property.
Start the ]WED editor again, select the form object by clicking in it
(outside of any control) and select border from the Property combo
box. You may notice that the [...]button to the right of the Value
field gets enabled. Every time this button is enabled you can of course click on it.
Click on the [...]button. A form appears to let you change
elements of our dialog box border property. Change them so that they look like the
following:

Figure 12 - The ]WED border dialog box
then click the OK button.
The Value field should now display 3 16.
Testing your form without leaving ]WED
You may test your form even though you are still under control of the
]WED editor. Simply choose Test / Start or press Ctrl+T.
The following form should be displayed:

Figure 13 - Testing the dialog box from ]WED
Note that the form does no longer have any system menu, even though
we asked for one: this is because we also chose Modal border which has precedence over the
System Menu choice. Also the minimize and maximize buttons have disappeared. This is the
standard appearance of a modal dialog box (if you want to be convinced, load WinWord and
display any dialog box from the WinWord menus, and compare).
Note that the dialog box can no longer be resized.
Once you are satisfied with your test, simply close the form to
return to the ]WED design mode.
Summing up our application code so far
’ fmDb_Make;x;Śwself
[1] ©' fmDb_Make - Created 10/16/96 at 21:35:43
[2] 'fmDb' Świ 'Delete'
[3]
[4] Śwself„'fmDb' Świ 'New' 'Form' 'Close'
[5] Świ 'scale' 5 0 0 193 305
[6] Świ 'caption' 'My first Dialog Box'
[7] Świ 'border' 19
[8] Świ 'extent' 12.0625 38.125
[9] Świ 'where' 11 21.875
[10] Świ 'onFocus' 'fmDb_Focus'
[11]
[12] Śwself„'fmDb.bnOK' Świ 'New' 'Button'
[13] Świ 'caption' 'OK'
[14] Świ 'where' 10 220 25 75
[15] Świ 'onClick' 'fmDb_bnOK_Click'
[16]
[17] Śwself„'fmDb.bnCancel' Świ 'New' 'Button'
[18] Świ 'caption' 'Cancel'
[19] Świ 'where' 40 220 25 75
[20] Świ 'onClick' 'fmDb_bnCancel_Click'
[21]
[22] Śwself„'fmDb.lQst' Świ 'New' 'Label'
[23] Świ 'caption' 'Question'
[24] Świ 'where' 145 5 15 290
[25]
[26] Śwself„'fmDb.edQst' Świ 'New' 'Edit'
[27] Świ 'where' 165 5 20 290
’
’ fmDb_Focus
[1] 'fmDb.edQst'Świ'Focus'
’
’ fmDb_bnOK_Click;A
[1] A„'fmDb.edQst'Świ'text'
[2] 'fmDb'Świ'value' A
[3] 0 0˝'fmDb'Świ'Close'
’
’ fmDb_bnCancel_Click
[1] 'fmDb'Świ'value' (0 0˝0)
[2] 0 0˝'fmDb'Świ'Close'
’
Supplying an initialization value for the edit control
Another enhancement to our Windows application would be to be able to
specify a default answer to the question. This default answer should be passed as a
parameter to the fmDb_Make function and it should be displayed in the edit control as soon
as the dialog box shows up.
How do we do that?
We must provide this initialization value at the time the form is launched, i.e. when the Wait
method is invoked. This is done by specifying the initialization value as an argument to
the Wait method.
This argument can be read in the onWait event handler. Its value is then supplied
in the WARG system variable.
We therefore need to do 2 things:
- pass the initialization value as an argument to the Wait method
- define an event handler for the onWait event, reading the
argument from the WARG
system variable
This time we will directly edit the fmDb_Make function.
Change it so that it looks like the following (changes have been set to bold characters):
’ R„A fmDb_Make B;x;Śwself
[1] ©’R„A fmDb_Make B -- Created 10/16/96 at 21:35:43
[2] 'fmDb' Świ 'Delete'
[3]
[4] Śwself„'fmDb' Świ 'New' 'Form' 'Close'
[5] Świ 'scale' 5 0 0 193 305
[6] Świ 'caption' 'My first Dialog Box'
[7] Świ 'border' 19
[8] Świ 'extent' 12.0625 38.125
[9] Świ 'where' 11 21.875
[10] Świ 'onFocus' 'fmDb_Focus'
[11] Świ 'onWait' 'fmDb_Wait'
[12]
[13] Śwself„'fmDb.bnOK' Świ 'New' 'Button'
[14] Świ 'caption' 'OK'
[15] Świ 'where' 10 220 25 75
[16] Świ 'onClick' 'fmDb_bnOK_Click'
[17]
[18] Śwself„'fmDb.bnCancel' Świ 'New' 'Button'
[19] Świ 'caption' 'Cancel'
[20] Świ 'where' 40 220 25 75
[21] Świ 'onClick' 'fmDb_bnCancel_Click'
[22]
[23] Śwself„'fmDb.lQst' Świ 'New' 'Label'
[24] Świ 'caption' 'Question'
[25] Świ 'where' 145 5 15 290
[26]
[27] Śwself„'fmDb.edQst' Świ 'New' 'Edit'
[28] Świ 'where' 165 5 20 290
[29]
[30] R„'fmDb'Świ'Wait' A
’
Tip once you start making manual changes to your fmDb_Make
function, you will not be able to use ]WED any more without risking to override the manual
changes to fmDb_Make.
Note We will be using the function left argument to pass the initialization string
(default answer) to it. Thr right argument will for now be the empty character string and
will be used later.
Let's define the fmDb_Wait event handler function as follows:
’ fmDb_Wait
[1] 'fmDb.edQst'Świ'text' Śwarg
’
Now run the fmDb_Make function again to apply changes to the fmDb
form and run the form:
'APL+Win' fmDb_Make ''
Our dialog box instantaneously displays with its initialization value
in the edit control:

Figure 14 - Testing the dialog box with a default answer
However this is not quite perfect yet. Most well behaved
Windows application would display the default answer, selected, with the caret positioned
at the end of the selected characters.
In order to do this we probably need to use some of the characteristics of the edit
control object. Let's look at the Windows Interface help file provided with APL+Win.
Select Help / Windows Interface
from the APL+Win session menus. In the help file, select Classes, then Edit.
The following help page displays:

Figure 15 - The APL+Win Windows Interface help file
It show us that the selection property is exactly what we are
looking for. So, we should use it on the edit control, once it is initialized. Where
should we change this property? Just after initializing the edit control, in the
fmDb_Wait
event handler, of course.
Change the fmDb_Wait event handler to the following:
’ fmDb_Wait;A
[1] 'fmDb.edQst'Świ'text' (A„Śwarg)
[2] 'fmDb.edQst'Świ'selection' (0,˝A)
’
Is our dialog box complete yet? Not quite: we would also like that
the Enter key behaves as a click on the OK button and that the Esc
key behaves as a click on the Cancel button.
Accepting the Enter and Esc keys
If we know enough of Windows programming, we should be aware that it
is possible to have one button in a form which is the default button and accepts the Enter
key. It is also possible to have one button which accepts the Esc key and behaves
as a Cancel button.
If we search in the Windows Interface help file or in the APL+Win
documentation, looking for properties of the Button class, we find that buttons may
have one of the following values set in their style property:
Button Styles
| Value |
Description |
|
| 1 |
Default button for the entire form (Enter key generates a click on
this button) |
| 2 |
Cancel button (Esc key generates a click on this button) |
Let's manually change the fmDb_Make function, adding these style
properties to our fmDb.bnOK and fmDb.bnCancel buttons.
The function should now read:
’ R„A fmDb_Make B;x;Śwself
[1] ©’R„A fmDb_Make B -- Created 10/16/96 at 21:35:43
[2] 'fmDb' Świ 'Delete'
[3]
[4] Śwself„'fmDb' Świ 'New' 'Form' 'Close'
[5] Świ 'scale' 5 0 0 193 305
[6] Świ 'caption' 'My first Dialog Box'
[7] Świ 'border' 19
[8] Świ 'extent' 12.0625 38.125
[9] Świ 'where' 11 21.875
[10] Świ 'onFocus' 'fmDb_Focus'
[11] Świ 'onWait' 'fmDb_Wait'
[12]
[13] Śwself„'fmDb.bnOK' Świ 'New' 'Button'
[14] Świ 'caption' 'OK'
[15] Świ 'style' 1
[16] Świ 'where' 10 220 25 75
[17] Świ 'onClick' 'fmDb_bnOK_Click'
[18]
[19] Śwself„'fmDb.bnCancel' Świ 'New' 'Button'
[20] Świ 'caption' 'Cancel'
[21] Świ 'style' 2
[22] Świ 'where' 40 220 25 75
[23] Świ 'onClick' 'fmDb_bnCancel_Click'
[24]
[25] Śwself„'fmDb.lQst' Świ 'New' 'Label'
[26] Świ 'caption' 'Question'
[27] Świ 'where' 145 5 15 290
[28]
[29] Śwself„'fmDb.edQst' Świ 'New' 'Edit'
[30] Świ 'where' 165 5 20 290
[31]
[32] R„'fmDb'Świ'Wait' A
’
Let's try the dialog box again:
'APL+Win' fmDb_Make ''
Change the text in the edit control to: APL+Unix and press the
Enter key.
Now press the Esc key. Nice! The form behaves as if we had
clicked the OK button.
Start the form again:
'APL+Win' fmDb_Make ''
Now press the Esc key. The form behaves as if we had clicked
the Cancel button.
Summarizing our Windows application source code
The complete source code of all our dialog box is reproduced below:
’ R„A fmDb_Make B;x;Śwself
[1] ©’R„A fmDb_Make B -- Created 10/16/96 at 21:35:43
[2] 'fmDb' Świ 'Delete'
[3]
[4] Śwself„'fmDb' Świ 'New' 'Form' 'Close'
[5] Świ 'scale' 5 0 0 193 305
[6] Świ 'caption' 'My first Dialog Box'
[7] Świ 'border' 19
[8] Świ 'extent' 12.0625 38.125
[9] Świ 'where' 11 21.875
[10] Świ 'onFocus' 'fmDb_Focus'
[11] Świ 'onWait' 'fmDb_Wait'
[12]
[13] Śwself„'fmDb.bnOK' Świ 'New' 'Button'
[14] Świ 'caption' 'OK'
[15] Świ 'style' 1
[16] Świ 'where' 10 220 25 75
[17] Świ 'onClick' 'fmDb_bnOK_Click'
[18]
[19] Śwself„'fmDb.bnCancel' Świ 'New' 'Button'
[20] Świ 'caption' 'Cancel'
[21] Świ 'style' 2
[22] Świ 'where' 40 220 25 75
[23] Świ 'onClick' 'fmDb_bnCancel_Click'
[24]
[25] Śwself„'fmDb.lQst' Świ 'New' 'Label'
[26] Świ 'caption' 'Question'
[27] Świ 'where' 145 5 15 290
[28]
[29] Śwself„'fmDb.edQst' Świ 'New' 'Edit'
[30] Świ 'where' 165 5 20 290
[31]
[32] R„'fmDb'Świ'Wait' A
’
’ fmDb_Focus
[1] 'fmDb.edQst'Świ'Focus'
’
’ fmDb_Wait;A
[1] 'fmDb.edQst'Świ'text' (A„ Śwarg)
[2] 'fmDb.edQst'Świ'selection' (0,˝A)
’
’ fmDb_bnOK_Click;A
[1] A„'fmDb.edQst'Świ'text'
[2] 'fmDb'Świ'value' A
[3] 0 0˝'fmDb'Świ'Close'
’
’ fmDb_bnCancel_Click
[1] 'fmDb'Świ'value' (0 0˝0)
[2] 0 0˝'fmDb'Świ'Close'
’
As you can see, the APL+Win source code of our complete Windows
application fits comfortably on one page (and we have not even written the fmDb_Make main
function ourselves (it was auto generated by the ]WED editor: we just had to edit it).
But we can go one step further and I will describe this in the next
section.
Nature of Windows programming in APL
This section is extremely important to clearly understand if you
are new to Windows programming with APL.
If you happen to have used APL in the past on mainframes or under
DOS, and if you carefully observe what we have done so far, you may be struck by the
following discoveries about Windows programming with APL+Win:
Discovery 1 - everything in a Windows APL application is global
Observe our application: it is made of 5 APL functions. One,
fmDb_Make is the main function which builds the interface and starts the application. The
other 4 are callback functions (also called "event handlers"). Are they called
by the main function as subroutines (as it used to be the case in mainframe or DOS APL
applications)? No! Even if you look carefully, you will find NO line of code in our
application that specifically calls any of these 4 functions. There 4 functions are global
relatively to the main application function.
Let's explain a little bit better what happens when you launch
program fmDb_Make.
This program executes very fast to line 32 (a couple of
milliseconds maybe). Then it executes line 32 where the Wait method is
invoked on the form. At this stage the fmDb_Make program stops running and waits.
What does it wait for?
Events of course!
Everytime an event occurs, most often generated by the user action
(all the time in our simple application), like for example a key pressed or a mouse click,
the callback function (if any defined) associated with this particular event is
automatically run by the APL+Win system.
You must understand that clearly: the Wait method tells the APL+Win
system to start listening to events and when it hears that one is occurring on a given
object, to check if a callback function has been associated with this event on this object
(by such instructions as 'fmDb'wi'onFocus' 'fmDb_Focus' or
'fmDb.bnOK'wi'onClick' 'fmDb_bnOK_Click')
This wait state could end for ever and the fmDb_Make program would
never terminate. Happily there are a couple of methods which can close a form and
terminate the Wait state (like the Close method which we have used).
Discovery 2 - a Windows application has a large quantities of
callback functions
A very simple application like ours, which only uses one form and 4
controls, required that we wrote 4 callback functions.
The number of callback functions can be VERY large in a real life
Windows application.
To understand this better, first look at the Windows Interface help
file, select Classes and then any object like the Edit object, then click on the Events
word at the top. You will see all the kinds of events that can occur on an edit control.
Here they are:
onChange, onClose, onDdeConnect, onDdeDisconnect, onDestroy,
onFocus, onHide, onKeyDown, onKeyPress, onKeyUp, onLimit, onMemory, onMouseDown,
onMouseDrag, onMouseMove, onMouseUp, onMove, onOpen, onPaint, onResize, onScroll, onSend,
onShow, onUnfocus.
This means that for one particular edit control in a form, you may
have to write up to 24 event handlers.
Happily, you will never need to handle all these events, but a
typical Edit control might require that your application reacts to a few events such as
onChange, onFocus, onKeyPress, onLimit, onUnfocus
and maybe onScroll. The same remark
applies for other classes of controls, although the events may differ according to the
controls.
If your application typically has 100 forms, each containing about 10
controls (which is a very low average) and if we assume you need to handle a mean of 4
events per control, this means your application will require you to write 100x10x4 = 4000
callback functions!
This means that we must be prepared to be VERY WELL organized in
order to cope with workspaces containing 4000 callback functions, all of them being global
one to each other. What about callback function names: we MUST adopt a strict naming
conventions to avoid anarchy, otherwise we will soon be completely lost!
But happily, the next section will show techniques to avoid these
traps and make things look easy and nice.
Discovery 3 - Callback functions are small and easy to write
This is certainly true for our application. Our most complex callback
functions contains 3 simple APL instructions!
This is generally true of the vast majority of callback functions in
a real life Windows application. They most often have just a few instructions.
Occasionally there could be a couple of larger callback functions, but this is rare.
That is nice, because, except for creating the interface (like we did
with the ]WED editor and fmDb_Create) there is NOTHING ELSE in a Windows application than
callback functions.
This leads us to the next discovery.
Discovery 4 - The only place where we can run code in an APL+Win
Windows application is within callback functions
This means that, once the Wait method is invoked on the main form,
i.e. after a few milliseconds following the launch of the application, no other APL code
can run except callback functions, called by the APL+Win system.
Think about it! What does this clearly mean?
This means that, whatever you need to do, you MUST write an event
handler function and associate it with an event on an object.
Often your main concern will be to identify which event to handle on
which object, rather than to find out how to write the event handler code.
A consequence is that, in order to become a real good APL+Win
programmer, you will have to over time learn how Windows behaves and how the APL+Win
system behaves in terms of its communications with Windows.
But don't panic: you will be able to do a lot, from the early
beginning and are partly isolated from Windows thanks to the APL+Win high level oriented
object tools (WI, WARG,
...)
Discovery 5 - We work with new entities called objects without
first understanding exactly what they are
For the first time in APL, you create objects (forms, buttons,
labels, edit controls, ...) which show themselves on the screen and clearly exist, without
you knowing exactly what they are.
Have you asked yourself: where do I find these objects in my APL
workspace? You clearly understand about functions and APL variables, because you can see
them with such commands as )FNS or )VARS , but what about our Windows objects?
From the good old principle that something cannot come from nothing,
the creation of these objects must correspond to the reduction of some kind of resources
available to us.
Knowing that, in terms of software, everything you create directly
relates to memory and finally is nothing else than a shorter or longer series of 0s and 1s
stored somewhere in your computer, you can guess that APL+Win forms, buttons, edit
controls, labels, ... are nothing else than MEMORY.
Whenever you create an Windows object, you are using memory resources
in your machine.
Now, let's try an easy experiment that many APL+Win novices have
tried and which generally disconcert them:
Śwa
6250012
'ff'Świ'New' 'Form'
ff

Figure 16 - Testing workspace memory used by a form
Śwa
6250012
Here is an apparent paradox. We are sure that an object uses some
memory, but we are able to create a new object and our available memory stays EXACTLY the
same.
If you think a few seconds about that paradox, you will easily find
the solution: APL+Win objects are created outside of the APL workspace, in some other
memory space. This is just deduction.
Let's verify if this is true.
)CLEAR
CLEAR WS

Figure 17 - Testing form surviving a )CLEAR
Our ff form is still on the screen: if it had been part of the
previous workspace, it would have been destroyed by )CLEAR, just like all other APL
objects (functions, variables, labels, ...) are.
Callbacks encapsulation
With the above discoveries in mind, let us revisit our dialog box
example.
What would really be nice would be to make a 1 to 1 relationship
between a dialog box and an APL program!
In other words, we do not want our dialog box to be represented by 5
programs, but by just 1 program. Doing this simplifies a lot of things for the Windows
applications APL developer.
- It will tremendously reduce the number of functions in the workspace
- it will make it much easier to transfer a form from one workspace to
another
- it will make it even easier to save forms on files (1 form = 1
function)
- it will make it much easier to copy paste between callbacks
- it will much reduce name conflicts in the workspace
- it will make it easier to master the whole applications
- most of whole, it will greatly increase the reusability of your forms
How can we encapsulate the 4 callbacks within the same function that
builds the interface?
Thanks to the APL+Win control structures, we can make a crystal clear
fmDb_Make function.
Let me show you a better version of our dialog box application and
them comment it.
’ R„A fmDb_Make B;x;Śwself
[1] ©'R„A fmDb_Make B -- Created 10/16/96 at 21:35:43
[2]
[3] :select B
[4] :case ''
[5] 'fmDb' Świ 'Delete'
[6]
[7] Śwself„'fmDb' Świ 'New' 'Form' 'Close'
[8] Świ 'scale' 5 0 0 193 305
[9] Świ 'caption' 'My first Dialog Box'
[10] Świ 'border' 19
[11] Świ 'extent' 12.0625 38.125
[12] Świ 'where' 11 21.875
[13] Świ 'onFocus' 'fmDb_Make''Focus'''
[14] Świ 'onWait' 'fmDb_Make''Wait'''
[15]
[16] Śwself„'fmDb.bnOK' Świ 'New' 'Button'
[17] Świ 'caption' 'OK'
[18] Świ 'style' 1
[19] Świ 'where' 10 220 25 75
[20] Świ 'onClick' 'fmDb_Make''bnOK.Click'''
[21]
[22] Śwself„'fmDb.bnCancel' Świ 'New' 'Button'
[23] Świ 'caption' 'Cancel'
[24] Świ 'style' 2
[25] Świ 'where' 40 220 25 75
[26] Świ 'onClick' 'fmDb_Make''bnCancel.Click'''
[27]
[28] Śwself„'fmDb.lQst' Świ 'New' 'Label'
[29] Świ 'caption' 'Question'
[30] Świ 'where' 145 5 15 290
[31]
[32] Śwself„'fmDb.edQst' Świ 'New' 'Edit'
[33] Świ 'where' 165 5 20 290
[34]
[35] R„'fmDb'Świ'Wait' A
[36]
[37] :case 'Focus'
[38] 'fmDb.edQst'Świ'Focus'
[39]
[40] :case 'Wait'
[41] 'fmDb.edQst'Świ'text' (A„ Śwarg)
[42] 'fmDb.edQst'Świ'selection' (0,˝A)
[43]
[44] :case 'bnOK.Click'
[45] A„'fmDb.edQst'Świ'text'
[46] 'fmDb'Świ'value' A
[47] 0 0˝'fmDb'Świ'Close'
[48]
[49] :case 'bnCancel.Click'
[50] 'fmDb'Świ'value' (0 0˝0)
[51] 0 0˝'fmDb'Świ'Close'
[52]
[53] :end
’
When the fmDb_Make function is called with an empty right argument
('') , then the form is built.
As you can see, the fmDb_Make function now also serves as a callback
function for each of our 4 events. The object name and nature of the event is passed as a
right argument to fmDb_Make. Depending on the right argument, a simple:select :case
:end control structure helps branch to the right callback code.
We have omitted the form name in the fmDb_Make right arguments,
because we know everything here is related to the form called fmDb (the only one used by
function fmDb_Make).
At this stage, and since our function has now really become a Windows
utility (I would be inclined to rather speak of a utility form) we should in fact change
the name of our function, which we inherited from ]WED, to something more meaningful and
more closely related to our form.
Let's rename our function: GuiInput
And let's make some adjustments and touches to this utility. It now
reads:
’ R„A GuiInput B;Śwself
[1] ©’R„A GuiInputB -- Created 10/16/96 at 21:35:43
[2]
[3] :select B
[4] :case ''
[5] :if 0=Śnc'A' Ş A„'' Ş :end
[6] 'fmDb' Świ 'Delete'
[7]
[8] Śwself„'fmDb' Świ 'New' 'Form' 'Close'
[9] Świ 'scale' 5 0 0 193 305
[10] Świ 'caption' 'My first Dialog Box'
[11] Świ 'border' 19
[12] Świ 'extent' 12.0625 38.125
[13] Świ 'where' 11 21.875
[14] Świ 'onFocus' 'GuiInput''Focus'''
[15] Świ 'onWait' 'GuiInput''Wait'''
[16]
[17] Śwself„'fmDb.bnOK' Świ 'New' 'Button'
[18] Świ 'caption' 'OK'
[19] Świ 'style' 1
[20] Świ 'where' 10 220 25 75
[21] Świ 'onClick' 'GuiInput''bnOK.Click'''
[22]
[23] Śwself„'fmDb.bnCancel' Świ 'New' 'Button'
[24] Świ 'caption' 'Cancel'
[25] Świ 'style' 2
[26] Świ 'where' 40 220 25 75
[27] Świ 'onClick' 'GuiInput''bnCancel.Click'''
[28]
[29] Śwself„'fmDb.lQst' Świ 'New' 'Label'
[30] Świ 'caption' 'Question'
[31] Świ 'where' 145 5 15 290
[32]
[33] Śwself„'fmDb.edQst' Świ 'New' 'Edit'
[34] Świ 'where' 165 5 20 290
[35]
[36] R„'fmDb'Świ'Wait' A
[37]
[38] :case 'Focus'
[39] © 'fmDb.edQst'Świ'Focus'
[40] Świ'.edQst.Focus'
[41]
[42] :case 'Wait'
[43] © 'fmDb.edQst'Świ'text' (A„ Śwarg)
[44] © 'fmDb.edQst'Świ'selection' (0,˝A)
[45] Świ'.edQst.text' (A„ Śwarg)
[46] Świ'.edQst.selection' (0,˝A)
[47]
[48] :case 'bnOK.Click'
[49] © 'fmDb'Świ'value'('fmDb.edQst'Świ'text')
[50] © A„'fmDb'Świ'Close'
[51] Śwself„'fmDb'
[52] Świ'value'(Świ'.edQst.text')
[53] A„Świ'Close'
[54]
[55] :case 'bnCancel.Click'
[56] © 'fmDb'Świ'value' (0 0˝0)
[57] © A„'fmDb'Świ'Close'
[58] Śwself„'fmDb'
[59] Świ'value' (0 0˝0)
[60] A„Świ'Close'
[61]
[62] :end
’
We have made the following cosmetic adjustments to this function:
- we have deleted the localized x variable (erroneously created by ]WED)
- we have added a test to initialize left argument A (so that the
function my be called with no left argument)
- we have replaced 0 0˝ notation (which I dislike) by A in a
couple of places
- we have commented our callback functions lines and used an alternative
WI notation which only uses a right argument
Other improvements
To make it a real utility, we need some final adjustments to our
function.
- We need to pass additional elements in the left argument, such as:
- the form's caption
- the question text
- We need to add comments
Here is the final version of the function:
’ R„A GuiInput B;C;D;E;Śwself
[1] ©’R„A GuiInput B -- Created 10/16/96 at 21:35:43
[2] ©’ Displays a form with a question and returns user answer
[3] ©’ It returns 0 0˝0 if user pressed Esc/clicked the Cancel button
[4] ©’ A „… 3-element nested vector of vectors
[5] ©’ A[1] „… default answer (character string; '' if omitted)
[6] ©’ A[2] „… form caption
[7] ©’ A[3] „… question
[8] ©’ B „… '' meaning: build the interface
[9] ©’ or 'object.event' meaning: handle a callback
[10] ©’
[11] ©’
[12] ©’ fax: (33.1)46.04.60.23 email: lescasse@uniware.fr
[13] ©’ (c) 1996 Eric Lescasse [17oct96]
[14]
[15] :select B
[16] :case ''
[17] :if 0=Śnc'A' ŞA„'' 'Question' 'Question' Ş :end
[18] (C D E)„A,(˝,A)‡'''Question' 'Question'
[19] 'fmDb' Świ 'Delete'
[20]
[21] Śwself„'fmDb'Świ 'New' 'Form' 'Close'
[22] Świ 'scale' 5 0 0 193 305
[23] Świ 'caption' D
[24] Świ 'border' 19
[25] Świ 'extent' 12.0625 38.125
[26] Świ 'where' 11 21.875
[27] Świ 'onFocus' 'GuiInput''Focus'''
[28] Świ 'onWait' 'GuiInput''Wait'''
[29]
[30] Śwself„'fmDb.bnOK' Świ 'New' 'Button'
[31] Świ 'caption' 'OK'
[32] Świ 'style' 1
[33] Świ 'where' 10 220 25 75
[34] Świ 'onClick' 'GuiInput''bnOK.Click'''
[35]
[36] Śwself„'fmDb.bnCancel' Świ 'New' 'Button'
[37] Świ 'caption' 'Cancel'
[38] Świ 'style' 2
[39] Świ 'where' 40 220 25 75
[40] Świ 'onClick' 'GuiInput''bnCancel.Click'''
[41]
[42] Śwself„'fmDb.lQst' Świ 'New' 'Label'
[43] Świ 'caption' E
[44] Świ 'where' 145 5 15 290
[45]
[46] Śwself„'fmDb.edQst' Świ 'New' 'Edit'
[47] Świ 'where' 165 5 20 290
[48]
[49] R„'fmDb'Świ'Wait' C
[50]
[51] :case 'Focus'
[52] Świ'.edQst.Focus'
[53]
[54] :case 'Wait'
[55] Świ'.edQst.text' (A„ Śwarg)
[56] Świ'.edQst.selection' (0,˝A)
[57]
[58] :case 'bnOK.Click'
[59] Śwself„'fmDb'
[60] Świ'value'(Świ'.edQst.text')
[61] A„Świ'Close'
[62]
[63] :case 'bnCancel.Click'
[64] Śwself„'fmDb'
[65] Świ'value' (0 0˝0)
[66] A„Świ'Close'
[67]
[68] :end
’
Conclusion
I hope you have enjoyed this dialog box tutorial.
You now know the basic concepts of dialog box programming with
APL+Win and you can get started design your wonderful own Windows GUI dialog boxes.
|