MMCC MASTHEAD
Mid-Michigan Computer Consultants - Bay City, Michigan
 


CONTENTS       (old style)
Mid-Michigan Computer Consultants
509 Center
Bay City, Michigan

Sales (989) 892-9242
Support (989) 686-8860

Plb-0370.cfm v1.0


plb-t010.cfm
 

ANSI Standard PL/B Language and Visual PL/B

E-Mail

This section discusses the TAB CONTROL.

Here are the hand written notes. These came mostly from a discussion with Steve Sansom at Sunbelt in Mid September 1988.

More discussion will come soon.
PURPOSE
Makes better use of screen space. You put up a box with "tabs" at the top. Click on a tab and that subject comes to the front.

This is a very commonly used WINDOWS convention. In particular it shows up in many of W95's own stuff.

THEORY
The TAB CONTROL is similar to a panel. The form itself is nothing but a rectangle with tabs at the top.

The TABS have no names unless you give them names. You can do that in the designer or the program. When done in the program the tabs are simply numbered.

The DATA SCREENS that go on the various tabs are NOT RELATED to the tab control form itself. They are loaded onto the main form and positioned where the tab control will be. You need to pay attention to sized and you need to make these PLForms objects only.

When the program loads you start by loading the main form. This gives you the tab control block with nothing on it.

Next you load all of the "sub-forms" that go on the tab control. You load them onto the main form by name. If you just leave them alone they will all be visible. In practice you load a form and disable it immediately so it doesn't show.

When a tab is clicked you get two events. First you get a CLICK telling you the tab you're leaving. Next you get a CHANGE telling you the tab you're entering. The Eventresult gives the tab number (1, 2, 3, 4, etc.)

When leaving a tab you can do something if you want, like get objects and give errors, etc. If you're DONE with that form, you can just disable it so it dissapears.

When ENTERING a new tab you want to ENABLE that form so that it shows.

In practice you hide the tab you're leaving and show the one you're entering.

The forms on the tabs have no relation to the tab control. They're just loaded on the main form. When a tab is clicked you get the event, hide forms you don't want and show the one you do want. In practice we once hid the specific form we were leaving. The new method as of 1/29/2012 is to just disable (hide) ALL of the forms on exiting a tab. Next we'll show the one we move into.

DESIGNER SETUP
Drag the tab box onto the screen like you would any other box.

Define the tabs in the TABDATA property, which is a list. The number of tabs you have in the list is the number on the tab control.

You should give each tab a name of some sort. In practice we just number the tabs when building the form then give them specific names when the form is loaded by the program. You can also GETITEM to get the current caption back:
     SETITEM {tabcontrolname}, {number of tab}, "TEXT STRING"
     GETITEM {tabcontrolname}, {number of tab}, "TEXT STRING"	 
This list ls like the one for a combo box or other box. You have buttons for adding and deleting names. Double click on it in the property list to get a box up. Use the buttons or just type a name and tap enter to insert it into the list.

Once you've defined the tabs, SAVE them. (They may not all show up immediatly in some versions of the designer, but they'll show up at some point.)

You can add more tabs later.

NOTE: You must set the MultipleRows property to true if you want more than one row.

FORMS THAT APPEAR ON THE TAB CONTROL TABS (or PAGES):


You must define the objects that appear on of each tab (or "page") of the tab control. These are done as separate forms.

Set the WindowType property on these FORMS to be Object Only. That will insure that they don't have messy borders or anything that lands on top of your tab control box. (One reason to do each form that is to be on a tab control page as a separate form.) You'll also load, hide and show these forms by name. (The other reason.)

You DO have to be concerned with the positioning on the screen. If you make the object too large or position it in the wrong place then it could look lousy when laid on top of the tab control.

In practice we design the main screen with the tab control. Then we note the height and width of the form and the position of the tab control. Each sub-form is designed to the same dimensions as the MAIN form. The objects are then designed to coincide with the position of the tab control. (We're doing objects only, after all).

We typically the first tab control form then clone it several times and use the clones for each page that goes on top of the tab control box. That helps insure correct size as long as we don't change the original sizes.

The designer gives you all the sizes and positions in the properties box for each object. You can check those numbers to be sure that things line up properly.

Other Designer Considerations

The tab control is like a group of several forms. The main form is considered the "parent" form. Each of the sub-forms are "children" and are loaded onto the parent. The tab control is an object on the parent.

Look at these properties on the child window:
  • WindowPos tells the runtime how to position the window on the screen. The choices are:
    • Absolute which will place the window in the same position that it's in when you build it in the designer

    • Center which will place the window in the center of the current screen regardless of the resolution or anything else.

    • Parent Center which will center the child window on the parent window. Note that the actual size of the entire child window should be the same as the parent window. (Setting WindowType (below) to Objects only will insure that the surrounding parts of the window don't show.)


    WindowType defines what parts of the window to use. The choices are:
    • Modal Dialog
    • Modeless Dialog
    • Objects Only
    • Primary Fixed
    • Primary Sizable

    For the child screens we want to use the Objects Only property. This will display only the object and allow them to sit on top of the tab control in the parent window.

    Z-Order May have some effect but we're not sure if it matters since the child/parent window relationship probably insures what goes on top.

PROGRAMMING



Loading the forms
When the program starts you can load all of your forms. Start with the main form (parent) as usual. Then load all of the sub-forms (children) onto the main form. Disable each after loading so they don't show up. This procedure insures that the forms are all loaded and ready to run but they don't show on the screen.

The order in which you load the sub-forms doesn't matter. You just want them loaded and hidden. The appropriate form will be activeated when a tab is clicked.

As a last step you can enable the form/tab that you want to start with.

Processing Tab Clicks


The tab control itself doesn't do anything. It's just a box on the screen. You lay other screen objects on top of it.

When the forms were loaded we hid (deactivated) all the forms except the one that we want to be showing.

When the user clicks a tab you'll get two events. First is for the tab they're leaving. Second is the tab that they're going to. There should be actions in the form for those two events.

You can tell the number of the tab that is currently being reported by checking the value of #EventResult. This is a local variable within the designer code only. It's a FORM 12 field.

If you want to have access to this value outside of the code attached to the form you should put in a code segment in the form to save the value somewhere. We use the following code in the form for the CLICK and the CHANGE events in the forms designer:
   For the CLICK action (leaving the tab):
         MOVE      "TAB-CLICK",   ACTION_TEXT
         MOVE      #EventResult,  EVENT_RESULT

   For the CHANGE action (going to a new tab):
         MOVE      "TAB-CHANGE",  ACTION_TEXT
         MOVE      #EventResult,  EVENT_RESULT
On the CLICK event #EventResult will be the number of the object that you're LEAVING. Use the CLICK event to DE-activate whatever form is currently displayed.

On the CHANGE event #EventResult will be the number of the object that you're GOING TO. Use the CHANGE event to ACTIVATE the new form for the tab.

Although not guaranteed, practice has shown that the events occur in the order shown: CLICK then CHANGE.

OTHER NOTES


Seems to be the concensus that you should do all your SAVE and other processing indendently of the tabs. That is, you can have a tab control with several pages. BELOW the tab control you could have a SAVE and CANCEL button.

The user would tab around in the control from one page to another setting data. When they're all done they would click the SAVE or CANCEL at the bottom and you can process ALL of the pages at that time.

That seems to be most consistent with the way that the majority of windows apps work.


EXAMPLE

(These were not updated 1/29/2012 when the above stuff was done)

For our purposes let's use a main form (window) called "W20" which has a tab control with two tabs.

The two tabs will be represented by two more forms called "W21" and "W22".
In the designer:
We would put code with two events in the designer for the W20 parent window. This code goes with the tab control.

On the CLICK event we'd code:
         MOVE      "TAB-CLICK",   ACTION_TEXT
         MOVE      #EventResult,  EVENT_RESULT

On the CHANGE event we'd code:
         MOVE      "TAB-CHANGE",  ACTION_TEXT
         MOVE      #EventResult,  EVENT_RESULT

In the mainline program:
In our mainline program we define each of the forms:
FORMW20   FORM       FORMW20.PLF

FORMW21 FORM FORMW21.PLF
FORMW22 FORM FORMW22.PLF

The first thing that we do in the body of the program is to load the forms and get everything active:
          FORMLOAD   FORMW20


FORMLOAD FORMW22, FORMW20_WINDOW
DEACTIVATE FORMW22

FORMLOAD FORMW21, FORMW20_WINDOW
We start with FORMW20 which is the parent window. The two tab windows are loaded next and each names the main window. This makes them children of the parent.

Note that we loaded the forms in reverse order and deactivated the higher number forms as they were loaded. This left them loaded but not showing. If there were six tabs we'd load 6, 5, 4, 3, 2, 1 and leave tab 1 active.
Next we could do any other house keeping code that would be appropriate before getting the program running.

The main loop of the program is a typical EVENTWAIT. The way we do it is like this:
         LOOP

EVENTWAIT
IF (ACTION_DONE = $TRUE |:
ACTION = "MENU")
BREAK
ENDIF

IF (ACTION = "SAVE")
BREAK
ELSE IF (ACTION = "CANCEL")
BREAK
ELSE IF (ACTION = "CLICK-TAB")
CALL CLICK_TAB_ACTION
ELSE IF (ACTION = "CHANGE-TAB")
CALL CHANGE_TAB_ACTION
ENDIF
REPEAT
{end of run code}
In that loop we are waiting for things to happen. The code in the form itself will do very little. Each object usually has just one line of code which moves a name to the "ACTION" field. We test here to see which field caused the EVENTWAIT to trigger.

The CLICK-TAB and the CHANGE-TAB actions are done on those two events for the tab control object.
The code associated with the CLICK-TAB and the CHANGE-TAB will be in charge of getting the proper child window active.

Remember that the CLICK will identify the tab that was previously active. On the screen that was the tab that was "in front".

The CHANGE event identifies the tab that was just clicked.

What we have to do is to deactivate the child form which was just left and activate the child form that was clicked. We do it like this:
CLICK_TAB_ACTION

IF (EventResult = 1)
DEACTIVATE FORMW21
ELSE IF (EventResult = 2)
DEACTIVATE FORMW22
ENDIF
RETURN
CHANGE_TAB_ACTION
IF (EventResult = 1)
ACTIVATE FORMW21
ELSE IF (EventResult = 2)
ACTIVATE FORMW22
ENDIF
RETURN
What we're doing is just to drop the child form that's up and bring up the one that the user wants to see.

The "EventResult" field that we're testing is setup by the code in the form itself. Remember that the form has the "#EventResult" avaible. But that's a local variable to the form only.

To get the information from the form to the mainline, we move the "#" variable to a regular (non-#) variable that all of the routine can see.

More code is required to do the rest of the processing. For example, all of the child windows can be pre-loded with data if you're bringing up an existing record. When the SAVE action is clicked all of the data can be retrieved from the CHILD windows and processed then.

Write to MMCC Technical Support at:               Send e-mail to MMCC.
MMCC - Technical Support
600 W. Midland
Bay City, MI 48708
(989) 686-8860
© 1997 - 2017 MMCC - All Rights Reserved