User Tools

Site Tools


creating_20a_20tab_20control

Creating a tab control

by Richard Russell, November 2006

A Tab Control is similar to a Property Sheet, but it is less intelligent: that is, you have to do more of the work yourself! The advantage is that it gives you extra flexibility: for example a Property Sheet contains built-in OK and Cancel buttons and (more importantly) processes them in a standard way; a Tab Control does not. When a Property Sheet does exactly what you need it will almost certainly be the best choice, but when it doesn't a do-it-yourself Tab Control may be the answer.

A simple tab control looks like this (using Windows XP visual styles):



Clicking on each tab brings the appropriate 'page' to the front. Creating a blank tab control (as shown) is straightforward but adding controls and other items to the individual pages is less so. These are the steps you must follow:

Initialisation


Firstly the necessary initialisation:

        INSTALL @lib$+"WINLIB2A"
        INSTALL @lib$+"WINLIB5A"
 
        ICC_TAB_CLASSES = 8
        GWL_STYLE = -16
        SW_HIDE = 0
        SW_SHOW = 5
        TCIF_TEXT = 1
        TCM_INSERTITEMA = &1307
        TCM_GETCURSEL = &130B
        WS_CLIPSIBLINGS = &4000000
        WS_CLIPCHILDREN = &2000000
        WS_SIZEBOX = &40000
        WS_MAXIMIZEBOX = &10000
 
        DIM icex{dwSize%, dwICC%}
 
        icex.dwSize% = DIM(icex{})
        icex.dwICC% = ICC_TAB_CLASSES
        SYS "InitCommonControlsEx", icex{}

Although not essential, trapping errors is desirable; otherwise an error message may be hidden behind the tab control:

        ON ERROR SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : QUIT


Creating the tab control


You create an 'empty' tab control as follows:

        Width% = 500
        Height% = 500
        hTC% = FN_createwindow(@hwnd%, "SysTabControl32", "", 0, 0, Width%, Height%, 0, \
        \                      WS_CLIPSIBLINGS + WS_CLIPCHILDREN, 0)
        IF hTC% = 0 ERROR 100, "Couldn't create tab control"

The width and height values are in pixels.

At this stage the control doesn't have any tabs; to add them use code similar to the following:

        PROCadd_tab(hTC%, "First", 1)
        PROCadd_tab(hTC%, "Second", 2)
        PROCadd_tab(hTC%, "Third", 3)

Here the second parameter of each call is the text string that appears on the tab, and the third parameter is an ID value for each tab. The PROCadd_tab routine is listed later.

Resizing your main window


You may, optionally, want to resize your main output window so that it is the same size as the tab control. If you do you can use the following code:

        DIM rc{l%,t%,r%,b%}
        SYS "GetWindowRect", hTC%, rc{}
        SYS "GetWindowLong", @hwnd%, GWL_STYLE TO style%
        SYS "SetWindowLong", @hwnd%, GWL_STYLE, style% AND NOT (WS_SIZEBOX + WS_MAXIMIZEBOX)
        SYS "AdjustWindowRect", rc{}, style% AND NOT (WS_SIZEBOX + WS_MAXIMIZEBOX), 0
        SYS "SetWindowPos", @hwnd%, 0, 0, 0, rc.r%-rc.l%, rc.b%-rc.t%, 102

This code also disables the ability to resize or maximise the window.

Handling page changes


This is where it gets interesting! Unlike a Property Sheet, changing what is displayed on each page as the tabs are clicked by the user is your responsibility. You must monitor the page changes, and automatically hide the contents of the previous page and show the contents of the new page as required. The basic code to monitor changes is as follows:

        prevsel% = -1
        REPEAT
          SYS "SendMessage", hTC%, TCM_GETCURSEL, 0, 0 TO currsel%
          IF currsel% <> prevsel% THEN
            REM. Hide contents of previous tab 'prevsel%'
            REM. Show contents of new tab 'currsel%'
            prevsel% = currsel%
          ENDIF
        UNTIL INKEY(1) = 0
        END

Here two variables prevsel% and currsel% keep track of the previous and current selections respectively.

There are two main approaches to drawing on the pages. One is to draw individual controls (buttons, boxes etc.) and the other is to display a dialogue box on each page, where the dialogue boxes contain the controls. Using dialogue boxes has the advantage of providing the standard navigation controls (e.g. using Tab) but is complicated by the DPI issue (see later).

To start off with here is a simple example of displaying an individual button on the second tab (currsel% = 1):

        hbutton% = FN_button(hTC%,"Tab Two",50,50,100,24,100,0)
        SYS "ShowWindow", hbutton%, SW_HIDE
 
        prevsel% = -1
        REPEAT
          SYS "SendMessage", hTC%, TCM_GETCURSEL, 0, 0 TO currsel%
          IF currsel% <> prevsel% THEN
            IF prevsel% = 1 SYS "ShowWindow", hbutton%, SW_HIDE
            IF currsel% = 1 SYS "ShowWindow", hbutton%, SW_SHOW
            prevsel% = currsel%
          ENDIF
        UNTIL INKEY(1) = 0
        END

Here the button is hidden when the previous selection is 1, and shown when the current selection is 1. Hopefully you can see how the code could be extended to cover multiple controls on multiple pages. See the description of the WINLIB5A library for details of the various controls that can be incorporated.

In the alternative case the code to display a dialogue box on each tab would be as follows:

        FOR index% = 0 TO DIM(dlg%(), 1)
          !(dlg%(index%)!4+8) = hTC%
          PROC_showdialog(dlg%(index%))
          SYS "ShowWindow", !dlg%(index%), SW_HIDE
        NEXT
 
        prevsel% = -1
        REPEAT
          SYS "SendMessage", hTC%, TCM_GETCURSEL, 0, 0 TO currsel%
          IF currsel% <> prevsel% THEN
            IF prevsel% >= 0 SYS "ShowWindow", !dlg%(prevsel%), SW_HIDE
            SYS "ShowWindow", !dlg%(currsel%), SW_SHOW
            prevsel% = currsel%
          ENDIF
        UNTIL INKEY(1) = 0
        END

Here dlg%() is an array containing the values returned from FN_newdialog(). Note the special piece of 'magic' code preceding the PROC_showdialog() which is essential to make this work.

You should create your dialogue boxes in the usual way, one for each tab; that can be done at the start of the program during the initialisation phase. See the description of the WINLIB2 library for details. Since the size of a dialogue box, in pixels, varies with the DPI (dots-per-inch) setting you may need to take special care to ensure the dialogue boxes fit the pages of the tab control (see Supporting different DPI values).

Procedures


Finally, the PROCadd_tab procedure:

        DEF PROCadd_tab(htc%, text$, id%)
        LOCAL cti{}, res%
        DIM cti{mask%, dwState%, dwStateMask%, pszText%, cchTextMax%, iImage%, lparam%}
        text$ += CHR$0
        cti.mask% = TCIF_TEXT
        cti.pszText% = !^text$
        SYS "SendMessage", htc%, TCM_INSERTITEMA, id%, cti{} TO res%
        IF res% = -1 ERROR 100, "Couldn't send Tab Control info"
        ENDPROC
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
creating_20a_20tab_20control.txt · Last modified: 2024/01/05 00:22 by 127.0.0.1