This document covers the following topics:
Tab controls are created in the
dialog editor
in the same way as other standard controls (such as list boxes or push buttons)
are. That is, they are either created statically in the
dialog editor
via the menu or by drag and drop from the Insert tool
bar, or dynamically at run-time by using a PROCESS GUI ACTION
ADD
statement with the
TYPE
attribute set to
TABCTRL
.
Alternatively, dialogs containing tab controls may be generated with the Dialog Wizard. In this case, many of the techniques described in this section are applied automatically by the wizard, and either do not need to be explicitly implemented, or simply need to be extended or "filled-out", whilst retaining the generated structure. This can significantly reduce the programming effort required.
A tab control may have zero or more tabs associated with it. Tabs may be
defined in the dialog editor from within the tab control's attribute window, or
at run-time by performing a PROCESS GUI ACTION
ADD
statement with the
TYPE
attribute set to
TABCTRLTAB
.
The tab control is a container. However, the individual tabs are not
containers, in the Natural implementation of this control. When controls are
created within the tab control in the dialog editor, the control's
PARENT
attribute is
automatically set to the handle of the tab control, and not to the handle of
the currently active tab (if any). In order to associate a child control with a
particular tab, the child control's
OWNER
attribute is set
to the handle of the tab with which the control should be associated. The
control is then automatically hidden by Natural when the tab is deactivated,
and automatically re-shown when it is re-activated.
Note, however, that the dialog editor only automatically sets the child
control's OWNER
attribute if the
tab control's "UI active (U)"
STYLE
flag is set,
which is the default setting. Otherwise the child control's
OWNER
attribute is left
unset (i.e., NULL-HANDLE
). In the latter case, the child control
is not automatically shown and hidden when switching between tabs. Note that
the "UI active (U)"
STYLE
has no effect at
run-time.
As stated above, all child controls within a tab control have a tab control as their parent, regardless of the tab to which they belong. Whilst this is sufficient, it may be preferable to separate the controls on different tabs into separate sub-hierarchies.
The most convenient way of achieving this is to create a child control
box for each tab to represent the tab "pages". All other child
controls are then created as child controls of the respective control box.
Assuming that the tab control's "UI active (U)"
STYLE
flag is set, the
control boxes will be automatically hidden and shown during tab switching, and
thus t heir respective child controls with them (child controls are
automatically hidden if any ancestor window is hidden). Otherwise, the program
must do the page switching explicitly, as described in the next section.
The control's organization is shown in the following diagram:
As shown in the diagram, each child control box should be transparent,
that the tab control's background texture (if any) shows through, and have the
"size to parent (z)"
STYLE
, so that the
control boxes automatically exactly fill the interior area of the tab control,
both immediately and whenever the size of the tab control is changed. In
addition, each control box should be "exclusive" if the tab
control is not UI active, such that only one child control box is visible at
any time.
Note:
This section only relates to tab controls that are not UI active.
Otherwise, the control switching is done automatically by Natural.
In the dialog editor, this is performed automatically by the dialog editor when a control belonging to an exclusive control box (or the control box itself) is selected, which is not currently being displayed. The dialog editor makes the currently visible exclusive control box (if any) invisible (thus also hiding any controls placed within it) and makes the control box containing the selected control visible (thus also showing any controls placed within it). This process is independent of how the selection is made (for example, explicitly, from the selection box in the status bar, or implicitly, by simply tabbing through the controls).
Note:
If the status bar is not shown, set the Status
Bar under the Dialog Editor tab of
the Options dialog opened via the
command.
At run-time, the control boxes must, of course, be shown or hidden in
response to the user selecting the corresponding tab. This can be achieved by
querying the active tab in the tab control's
CHANGE
event and setting the
VISIBLE
attribute of
the corresponding control box to TRUE
. One way of making the
tab/control box associations is to store the handle of the control box in the
CLIENT-HANDLE
attribute
of the corresponding tab in the
AFTER-OPEN
event of the dialog.
For example:
/* Map control boxes to tabs: #TAB-1.CLIENT-HANDLE := #CTLBOX-1 #TAB-2.CLIENT-HANDLE := #CTLBOX-2 .. #TAB-N.CLIENT-HANDLE := #CTLBOX-N
Then, assuming #CONTROL
is defined as HANDLE OF
GUI
, the
CHANGE
event of the tab control
(#TABCTRL-1
) could look like this:
/* Get active tab #CONTROL := #TABCTRL-1.SELECTED-SUCCESSOR /* Switch to control box belonging to active tab: #CONTROL := #CONTROL.CLIENT-HANDLE IF #CONTROL <> NULL-HANDLE #CONTROL.VISIBLE := TRUE END-IF
In some situations, it may be desirable to display controls that remain visible, irrespective of which tab is currently selected.
There are two ways of achieving this:
If control boxes are being used, the control boxes can be made smaller
in order to cover only part of the tab control's interior area, leaving the
remaining space available for controls that should be permanently displayed.
The "size to parent (z)"
STYLE
must be switched
off for the control boxes.
If control boxes are not being used and the tab control is UI active,
permanently displayed controls may be created by ensuring that the
"UI active (U)"
STYLE
for the tab
control is temporarily switched off whilst creating the child control(s) that
are to be permanently displayed.
If the tab control is not UI active, a two-layer control box hierarchy can be used, where the child control boxes described above are created as child controls of a transparent top-level control box, which in turn is created as a child of the tab control. The top-level control box (which does not have the "size to parent" flag set), can then be positioned and sized appropriately to define the replaceable region.
Note:
This is very similar to the technique used for wizard dialogs. See
the section Working with
Control Boxes for more information
There are three methods of navigating between the tabs of a tab control via the keyboard, any combination of which can be applied simultaneously:
If the tab control is assigned the "browsable
(z)" STYLE
flag, the tab
control is included in the tab sequence (i.e., can be navigated to via the
TAB key). When the tab control receives the focus, navigation
between the tabs is possible via the arrow keys. There is no
"wrap-around" between the first and last tabs in this case.
The tab captions (STRING
attribute) may
contain an ampersand (&), indicating that the following character is a
mnemonic character. The tab is selected when the mnemonic character is pressed
together with the ALT key. This allows for "direct"
keyboard navigation to the desired tab.
If the dialog has the "Property Sheet (p)"
STYLE
set, the keyboard
shortcuts CTRL+TAB and CTRL+SHIFT+TAB may be used to
navigate to the next and previous tab (respectively), with wrap-around.
Note that the focus does not have to be on the tab control or a dialog
element within it in order for the last technique to work. Starting from the
focus control, Natural examines each container (ancestor), until a container
(if any) is found that contains one or more tab controls. If this container
contains exactly one tab control, the keyboard shortcuts are then applied to
this tab control. If it contains two or more tab controls, these shortcuts have
no effect. If the dialog does not contain a tab control, the shortcuts perform
their usual function, as if the "Property Sheet (p)"
STYLE
had not been
set.
Note that this default usage of the CTRL+TAB and
CTRL+SHIFT+TAB key combinations may be overridden by redefining them
via the ACCELERATOR
attribute.
Whenever a tab switch is performed (either by the user or programmatically), the following sequence of events occurs:
The currently selected tab receives a
LEAVE
event, if not suppressed. This
event is typically used for data validation and/or committing the data on the
tab page.
The MODIFIABLE
attribute of
the tab control is then examined. If it is set to FALSE
, the tab
switch is not performed. The currently selected tab remains selected and no
further events are raised. This can be useful if data validation performed
during the LEAVE
event found an error, which should
be corrected by the user before continuing.
All direct child controls (if any) that have currently selected tab as
their OWNER
are automatically
hidden.
The new tab is selected.
All direct child controls (if any) that have the newly-selected tab as
their OWNER
are automatically
shown, if their VISIBLE
attribute is
set to TRUE
.
The newly-selected tab receives an
ENTER
event. If not suppressed. This
event is typically used for initializing controls on the new tab page.
The tab control receives a
CHANGE
event. This is convenient for
tracking tab switches and responding to them without having to modify the event
handlers for each tab.
Note that no initial
ENTER
event is raised for the selected
tab when the control is created, and that the tab control does not receive an
initial CHANGE
event either. Furthermore, when
the dialog containing the tab control is closed, the currently-selected tab
does not receive a
LEAVE
event.