v1.10 |
509 Center Bay City, Michigan Sales (989) 892-9242             Support (989) 686-8860 |
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.
DESIGNER SETUP
Drag the tab box onto the screen like you would any other box.
Define the tabs in the TABDATA property. Double click on it in the property list to get a box up. It's like several other multi-value objects; you type in a name and insert it into the list.
This is easier if you just type a name and tap enter. That automatically adds the tab to the list and gives you a blank box to do another. You can put them in really fast.
You don't have to give the tab a name (title, prompt, whatever) when you set them up. You can change that value on the fly. For example, you could make tabs colors or other things at run time.
SETITEM {tabcontrolname}, {number of tab}, "TEXT STRING"
You can also GETITEM to get the current caption back:
GETITEM {tabcontrolname}, {number of tab}, "TEXT STRING"
Once you've defined the tabs, SAVE them. They may not all show up immediatly in the designer but they'll show up at some point.
You can add more tabs later.
You must set the MultipleRows property to true if you want more than one row.
FORMS THAT APPEAR ON THE TAB CONTROL PAGES:
You should define the objects that go on top of each "page" of the tab control 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. That's the reason to do each form that is to be on a tab control page as a separate form.
You do have to be concerned with the positioning on the screen. If you make the object too large or position it wrong then it could look lousy when laid on top of the tab control.
You could make the tab control form then clone it several times and use the clone for each object that goes on top of the tab control box. That would insure correct size as long as you 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 ConsiderationsThe tab control will be a group of several forms. The first one will be considered the "parent" form. Each of the tabs will have a "child" window associated with it.
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 formsWhen the program starts you can load all of your forms. That is, load the parent and all the children.
We usually load the children in reverse order and deactivate each one right after loading. The last form loaded is the first tab and we do NOT deactivate it. This procedure insures that the forms are all loaded and ready to run but they don't show on the screen.
Processing Tab Clicks
The tab control itself doesn't do anything. It's just a box on the screen. You can lay other screen objects on top of it.
In the step above we loaded all the tab (child) windows and deactivated all the but last one loaded. The user will see the tab control and that one child window sitting on the tab control form.
You detect the tabs by testing the CLICK and CHANGE events on the tab control object itself.
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. 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.
For example in both the CLICK and the CHANGE events in the forms designer we insert code like this:For the CLICK action: MOVE "TAB-CLICK", ACTION_TEXT MOVE #EventResult, EVENT_RESULT
For the CHANGE action: 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.
We're not sure if you have any guarantee that CHANGE and CLICK will be reported in a predictable sequence or not. If all you do is ACTIVATE and DEACTIVATE it probably doesn't matter because the events will happen very fast.
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
For our purposes let's use a main form (window) called "W20" which has a tab control with two tabs.In the designer:
The two tabs will be represented by two more forms called "W21" and "W22".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_WINDOWWe 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.Next we could do any other house keeping code that would be appropriate before getting the program running.
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.
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 code associated with the CLICK-TAB and the CHANGE-TAB will be in charge of getting the proper child window active.
The CLICK-TAB and the CHANGE-TAB actions are done on those two events for the tab control object.
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
RETURNWhat we're doing is just to drop the child form that's up and bring up the one that the user wants to 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.
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.
v1.10 |
Send e-mail to MMCC. |