Hide

SftTabs/DLL 6.5 - Tab Control

Display
Print

Using C

This section describes how to use SftTabs/DLL in an application written using the C programming language.

Adding SftTabs/DLL to an Application

Please see "Building Applications" to prepare a project for development with SftTabs/DLL.

A)Every source program making use of a SftTabs/DLL control must include the required header file SftTabs.h by using the #include directive.
#include "SftTabs.h" /* SftTabs/DLL required header file */

This include statement should appear after the #include <windows.h> statement. The file is located in the directory \Program Files (x86)\Softelvdm\SftTabs DLL 6.5\Include (unless changed during the installation).The project settings may need to be updated so the #include file can be located (see "Building Applications" for more information).

B)In order to use SftTabs/DLL controls, an application must call the SftTabs_RegisterApp function. The call to this function is required so that SftTabs/DLL window classes can be registered. This call has to be made before any SftTabs/DLL controls are created. Add the following statement to your source code, where your application registers its window classes (normally during application initialization):
SftTabs_RegisterApp(hInstance); /* Use SftTabs/DLL with this application */
C)Once SftTabs/DLL controls are no longer needed, an application must call the SftTabs_UnregisterApp function. The call to this function is required so that SftTabs/DLL window classes can be unregistered and cleanup processing can take place. This call has to be made after all SftTabs/DLL controls have been destroyed (normally during application termination).
SftTabs_UnregisterApp(hInstance); /* No longer use SftTabs/DLL */
D)The application's executable (Exe or Dll) must be linked with the correct Lib file, depending on the target environment. Please see "Building Applications" for more information.The project settings may need to be updated so the Lib files can be located (see "Building Applications" for more information).

Adding a Tab Control

There are two methods to add a tab control to an application:

  • using dialog resources<li>using CreateWindow(Ex)
  • Adding a tab control using dialog resources is accomplished by using a resource editor to design a dialog. Once a tab control is created, its window handle can be obtained by using the Windows GetDlgItem function. For more information, see Creating a Dialog Resource.

Another method to create a tab control is by using the CreateWindow(Ex) Windows call:

hwndPreview = CreateWindow(TEXT(SFTTABS_CLASS), NULL,
    WS_CHILD, 0, 0, 0, 0, hwndMain,
    (HMENU) IDC_TABS, hInstance, NULL);

For more information on the various parameters used, see the Windows API documentation.

Handling Notifications

As with standard Windows controls, applications must respond to events and messages to cause controls to respond to user requests. For additional information, see Notifications.

Switching Between Tabs

Switching between tabs is fully automatic, however, an application may wish to prevent a user from switching to another tab. By responding to the WM_COMMAND, SFTTABSN_SWITCHING notification, an application can prevent completion of the tab switch by sending a WM_CANCELMODE message to the tab control.

case WM_COMMAND: {
    HWND hwndCtl = (HWND) lParam;
    int id = LOWORD(wParam);
    int code = HIWORD(wParam);
    if (hwndCtl) {
        switch (id) {
            case IDC_TAB:
                switch (code) {
                    case SFTTABSN_SWITCHING:// we're about to switch away from
                        // the current page. If you need to know what the new
                        // page will be use SftTabs_GetNextTab(hwndCtl).
                        if (!IsOKToSwitch())
                            SendMessage(hwndCtl, WM_CANCELMODE, 0, 0);
                        break;
                    case SFTTABSN_SWITCHED:// we switched to a new page
                        SftTabs_ActivatePage(hwndParent, hwndCtl, NULL, FALSE);
                        break;
                }
                break;
            case IDOK:
            case IDCANCEL:
                if (code == BN_CLICKED)
                    SendMessage(hwndParent, WM_COMMAND, id, 0);
                break;
        }
    }
    break;
}

An application has to make attached controls or dialogs visible when switching between tabs. The SftTabs/DLL API offers functions to manage dialogs and Windows controls that are attached to tabs. See Implementing Tabbed Dialogs and Implementing Tabbed Windows for more information.

Implementing Tabbed Dialogs

A tabbed dialog is created just like any other dialog. A tabbed dialog has a tab control with an available client area. In this client area, pages are displayed. Each tab has an attached page (although during development of a tabbed dialog, a tab doesn't require an attached page). As the user switches between tabs, the appropriate page is created, displayed and destroyed.

A tabbed dialog and each page have a dialog procedure. This makes conversion of existing dialogs and development of new pages very easy. Tabbed dialogs and pages are first designed using a resource editor. The section Creating a Dialog Resource describes how the necessary tabbed dialog and page (dialog) resources are created.

Once the necessary dialogs have been designed, the tab control layout can be defined using the SftTabs/DLL Wizard. The SftTabs/DLL Wizard also creates much of the code required to initialize the tab control. This code should be copied to the application (with possibly minor modifications).

Creating a Tabbed Dialog Dialog Procedure

The following sample code (from \Program Files (x86)\Softelvdm\SftTabs DLL 6.5\Samples\C\Dialog\Dialog.c) shows a typical dialog procedure used for a tabbed dialog. Most of the code has been created using the SftTabs/DLL Wizard and then copied into the application.

/**********************************************************************/
/*                          Frame Dialog Proc                         */
/**********************************************************************/

/*- Tab Control Initialization Data --------------------------------------------*/

static const SFTTABS_CONTROL CtlInit = {
    SFTTABSSTYLE_MODERN_I,               /* tab style */
    2,                                   /* number of rows */
    0,                                   /* number of tabs per row (if fFixed) */
    0,                                   /* width of left margin */
    0,                                   /* width of right margin */
    FALSE,                               /* same width for all tabs */
    TRUE,                                /* Client area wanted */
    FALSE,                               /* allow multiline label text */
    TRUE,                                /* use with dialog */
    FALSE,                               /* use specified background color only for text */
    FALSE,                               /* scrollable tabs */
    FALSE,                               /* hide scroll buttons */
    TRUE,                                /* bold font for active tab wanted */
    TRUE,                                /* fill rows completely */
    NULL,                                /* scroll button bitmap */
    NULL,                                /* Dialog data associated with active tab */
    NULL,                                /* Dialog window handle associated with active tab */
    NULL,                                /* Frame, used as client area */
    TRUE,                                /* Tooltips wanted */
    FALSE,                               /* drop text if it doesn't fit */
    FALSE,                               /* conditional scroll buttons */
    BMBUTTONSTYLE_STD,                   /* scroll button style */
    FALSE,                               /* display ... if truncated */
    TRUE,                                /* Flyby highlighting */
    FALSE,                               /* use client area colors in partially obscured frames */
    FALSE,                               /* scroll buttons on left side */
    -1,                                  /* row indentation */
    FALSE,                               /* don't show truncated pattern for clipped tab */
    FALSE,                               /* full size scroll buttons */
    TRUE,                                /* use themes on Windows XP */
    TRUE,                                /* use exact window region */
    FALSE,                               /* always show prefix _ */
    0,0,0,0,                             /* animation values */
    NULL,                                /* disabled button bitmap */
    TRUE,                                /* focus rectangle if the control has i/p focus */
    FALSE,                               /* TRUE if Close button wanted */
    FALSE,                               /* TRUE if Close button disabled */
    FALSE,                               /* TRUE if WM_CLOSE message wanted */
    FALSE,                               /* TRUE if Minimize, Restore, Close buttons are full size */
    SFTTABS_BUTTON_NEAR,                 /* scroll button alignment */
    SFTTABS_BUTTON_NEAR,                 /* Minimize, Restore, Close button alignment */
    FALSE,                               /* TRUE if Minimize button wanted */
    FALSE,                               /* TRUE if Minimize button disabled */
    FALSE,                               /* TRUE if Restore button wanted */
    FALSE,                               /* TRUE if Restore button disabled */
    NULL,                                /* Close, Minimize, Restore button bitmap */
    NULL,                                /* Close, Minimize, Restore disabled button bitmap */
    TEXT(""),                            /* scroll left button tooltip */
    TEXT(""),                            /* scroll button tooltip */
    TEXT(""),                            /* Close button tooltip */
    TEXT(""),                            /* Minimize button tooltip */
    TEXT(""),                            /* Restore button tooltip */
    0,                                   /* custom modifications */
    0,                                   /* forced height/width depending on tab style - 0 to ignore */
    FALSE,                               /* switch tabs on button release (or down if FALSE) */
    FALSE,                               /* Rendering compatible with pre-6.0 */
    FALSE,                               /* don't display clientarea border - select styles only */
};

static const SFTTABS_TAB Tab0 = {   /*The First One */
    SFTTABS_NOCOLOR, RGB(0,0,255),       /* background, foreground color */
    SFTTABS_NOCOLOR, RGB(0,0,255),       /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_LEFT, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page1_Callback,              /* user supplied tab callback */
    NULL,                                /* reserved */
    SFTTABS_NOCOLOR,                     /* Flyby foreground color */
    SFTTABS_NOCOLOR,                     /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    SFTTABS_NOCOLOR,                     /* gradient fill background color */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill background color, active tab */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill client area color */
    SFTTABS_NOCOLOR,
};
static const SFTTABS_TAB Tab1 = {   /*&Second */
    SFTTABS_NOCOLOR, RGB(255,0,0),       /* background, foreground color */
    SFTTABS_NOCOLOR, RGB(255,0,0),       /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_RIGHT, 0 },          /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page2_Callback,              /* user supplied tab callback */
    NULL,                                /* reserved */
    SFTTABS_NOCOLOR,                     /* Flyby foreground color */
    SFTTABS_NOCOLOR,                     /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    SFTTABS_NOCOLOR,                     /* gradient fill background color */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill background color, active tab */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill client area color */
    SFTTABS_NOCOLOR,
};
static const SFTTABS_TAB Tab2 = {   /*&Third */
    SFTTABS_NOCOLOR, RGB(128,128,0),     /* background, foreground color */
    SFTTABS_NOCOLOR, RGB(128,128,0),     /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page3_Callback,              /* user supplied tab callback */
    NULL,                                /* reserved */
    SFTTABS_NOCOLOR,                     /* Flyby foreground color */
    SFTTABS_NOCOLOR,                     /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    SFTTABS_NOCOLOR,                     /* gradient fill background color */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill background color, active tab */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill client area color */
    SFTTABS_NOCOLOR,
};
static const SFTTABS_TAB Tab3 = {   /*F&ourth */
    SFTTABS_NOCOLOR, RGB(0,255,255),     /* background, foreground color */
    SFTTABS_NOCOLOR, RGB(0,255,255),     /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page4_Callback,  /* user supplied tab callback */
    NULL,                                /* reserved */
    SFTTABS_NOCOLOR,                     /* Flyby foreground color */
    SFTTABS_NOCOLOR,                     /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    SFTTABS_NOCOLOR,                     /* gradient fill background color */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill background color, active tab */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill client area color */
    SFTTABS_NOCOLOR,
};
static const SFTTABS_TAB Tab4 = {   /*F&ifth */
    SFTTABS_NOCOLOR, RGB(0,0,128),       /* background, foreground color */
    SFTTABS_NOCOLOR, RGB(0,0,128),       /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page5_Callback,              /* user supplied tab callback */
    NULL,                                /* reserved */
    SFTTABS_NOCOLOR,                     /* Flyby foreground color */
    SFTTABS_NOCOLOR,                     /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    SFTTABS_NOCOLOR,                     /* gradient fill background color */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill background color, active tab */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill client area color */
    SFTTABS_NOCOLOR,
};
static const SFTTABS_TAB Tab5 = {   /*Si&xth */
    SFTTABS_NOCOLOR, RGB(128,0,0),       /* background, foreground color */
    SFTTABS_NOCOLOR, RGB(128,0,0),       /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page6_Callback,  /* user supplied tab callback */
    NULL,                                /* reserved */
    SFTTABS_NOCOLOR,                     /* Flyby foreground color */
    COLOR_INFOBK|0x80000000,             /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    SFTTABS_NOCOLOR,                     /* gradient fill background color */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill background color, active tab */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill client area color */
    SFTTABS_NOCOLOR,
};

The dialog procedure shown here initializes the tab control in its WM_INITDIALOG message handler.

BOOL CALLBACK MainDialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {

    case WM_INITDIALOG: {
        int index;
        HWND hwndTab;
        SFTTABS_TAB Tab;

        // Center this dialog
        CenterWindow(hwndDlg);

        hwndTab = GetDlgItem(hwndDlg, IDC_TAB);
                                         /* get the window handle */
        /* load the bitmaps/icons */
        m_hSampleBitmap = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP));
        m_hSampleIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ICON));

        /* Initialization is faster if we set redraw off */
        SendMessage(hwndTab, WM_SETREDRAW, (WPARAM)FALSE, 0);

        /* We are using new features */
        SftTabs_SetVersion(hwndTab, SFTTABS_6_5);

        index = SftTabs_AddTab(hwndTab, TEXT("The First One"));
        SftTabs_SetToolTip(hwndTab, index, TEXT("Demonstrates tabbing into and out of the tab page"));
        Tab = Tab0;
        Tab.graph.item.hBitmap = m_hSampleBitmap;
        SftTabs_SetTabInfo(hwndTab, index, &Tab);

        index = SftTabs_AddTab(hwndTab, TEXT("&Second"));
        SftTabs_SetToolTip(hwndTab, index, TEXT("Demonstrates how an application can prevent tab switching"));
        Tab = Tab1;
        Tab.graph.item.hIcon = m_hSampleIcon;
        SftTabs_SetTabInfo(hwndTab, index, &Tab);

        index = SftTabs_AddTab(hwndTab, TEXT("&Third"));
        SftTabs_SetToolTip(hwndTab, index, TEXT("This page is reset everytime you switch to it"));
        SftTabs_SetTabInfo(hwndTab, index, &Tab2);

        index = SftTabs_AddTab(hwndTab, TEXT("F&ourth"));
        SftTabs_SetToolTip(hwndTab, index, TEXT("A page with private OK, Cancel, Next and Previous page buttons"));
        SftTabs_SetTabInfo(hwndTab, index, &Tab3);

        index = SftTabs_AddTab(hwndTab, TEXT("F&ifth"));
        SftTabs_SetToolTip(hwndTab, index, TEXT("A page that has not yet been implemented"));
        SftTabs_SetTabInfo(hwndTab, index, &Tab4);

        index = SftTabs_AddTab(hwndTab, TEXT("Si&xth"));
        SftTabs_SetToolTip(hwndTab, index, TEXT("A page with nested tab controls and pages"));
        SftTabs_SetTabInfo(hwndTab, index, &Tab5);

        SftTabs_SetControlInfo(hwndTab, &CtlInit);

        SftTabs_SetCurrentTab(hwndTab, 0);

        // Make sure to turn redraw back on
        SendMessage(hwndTab, WM_SETREDRAW, (WPARAM)TRUE, 0);
        InvalidateRect(hwndTab, NULL, TRUE);

        // Activate current page
        SftTabs_ActivatePage(hwndDlg, hwndTab, NULL, TRUE);

        // Mark the window as a main, tabbed dialog (so accel. keys work) by registering it.
        // Register the dialog AFTER activating the current page
        SftTabs_RegisterDialog(hwndDlg);

        // Update Tab2 to use transition effects
        {
            SFTTABS_TAB Tab;
            SftTabs_GetTabInfo(hwndTab, 2, &Tab);
            Tab.animationStyleShow = SFTTABS_SLIDE_FROM_LEFT;
            Tab.animationTimeShow = 400;
            SftTabs_SetTabInfo(hwndTab, 2, &Tab);
        }

        // Update Tab5 to use transition effects
        {
            SFTTABS_TAB Tab;
            SftTabs_GetTabInfo(hwndTab, 5, &Tab);
            Tab.animationStyleShow = SFTTABS_EXPAND_CENTER;
            Tab.animationTimeShow = 400;
            SftTabs_SetTabInfo(hwndTab, 5, &Tab);
        }

        {
            // Animate the entire window
            // Animation works on windows 98, 2000 or better only

            HMODULE hUser32 = GetModuleHandle(TEXT("USER32.DLL"));
            if (hUser32) {
                typedef BOOL (WINAPI* LPFNANIMATEWINDOW)(HWND, DWORD, DWORD);
                LPFNANIMATEWINDOW lpfnAnimateWindow;
                lpfnAnimateWindow = (LPFNANIMATEWINDOW)GetProcAddress(hUser32, "AnimateWindow");
                if (lpfnAnimateWindow)
                    lpfnAnimateWindow(hwndDlg, 300, AW_HOR_POSITIVE);
            }
        }
        return FALSE;                        // WM_INITDIALOG, input focus already set
     }
    ... more ...

The call to SftTabs_ActivatePage makes the current page active. The SftTabs_RegisterDialog call registers the dialog for special tabbed dialog handling by SftTabs/DLL. After registering the dialog, TAB and ESCAPE key handling, default button setting and keyboard accelerator keys (Alt-x) will be performed by SftTabs/DLL.

A tabbed dialog should always return FALSE when handling the WM_INITDIALOG message. The focus has been set already by SftTabs/DLL, so returning FALSE will prevent Windows from setting the focus (to the wrong control).

The following WM_DESTROY message handler shows the required cleanup calls to delete all bitmaps that the application has loaded, and also unregisters the dialog from SftTabs/DLL:

    case WM_DESTROY:
        /* delete the bitmaps/icons */
        DeleteObject(m_hSampleBitmap);

        // Unregister, or the window properties used won't be removed
        SftTabs_UnregisterDialog(hwndDlg);

        // destroy all pages
        SftTabs_Destroy(hwndDlg, GetDlgItem(hwndDlg, IDC_TAB));
        break;
    ... more ...

The following WM_COMMAND message handler responds to notifications sent to the tabbed dialog by the tab control. When the user attempts to switch to another page by clicking a tab using the left mouse button or by using the keyboard, a SFTTABSN_SWITCHING notification is sent to the tabbed dialog. The dialog then calls SftTabs_DeactivatePage to test if the active page can be left. This call to SftTabs_DeactivatePage results in WM_QUERYENDSESSION and SftTabs_GetEndPageMessage messages being sent to the page's dialog procedure. Based on the dialog procedure's response, the page may or may not be deactivated. By handling the WM_QUERYENDSESSION or SftTabs_GetEndPageMessage message, a page can prevent a user from switching away from a tab.

If a new tab has become active, the tabbed dialog receives a SFTTABSN_SWITCHED notification, so the associated page can be made active by the call to SftTabs_ActivatePage.

    case WM_COMMAND: {
        // Parameter packing differs between 16-bit and 32-bit Windows
        HWND hwndCtl = (HWND) lParam;
        int id = LOWORD(wParam);
        int code = HIWORD(wParam);
        if (hwndCtl) {

            switch (id) {
            case IDC_TAB:
                switch (code) {
                case SFTTABSN_SWITCHING:// we're about to switch away from
                    // the current page.  If you need to know what the new
                    // page will be, use SftTabs_GetNextTab(hwndCtl).
                    if (!SftTabs_DeactivatePage(hwndDlg, hwndCtl))
                        // couldn't deactivate current page, so don't switch
                        SendMessage(hwndCtl, WM_CANCELMODE, 0, 0);
                    break;
                case SFTTABSN_SWITCHED:// we switched to a new page
                    SftTabs_ActivatePage(hwndDlg, hwndCtl, NULL, FALSE);
                    break;
                }
                break;

            case IDOK:
            case IDCANCEL:
                if (code == BN_CLICKED)
                    SendMessage(hwndDlg, WM_COMMAND, id, 0);
                break;
            }

        } else {
            switch (id) {
            case IDOK:
                // The currently active page will be called with a
                // WM_QUERYENDSESSION/SftTabs_GetEndPageMessage message to determine
                // whether it can be closed
                if (SftTabs_ClosePossible(hwndDlg, GetDlgItem(hwndDlg, IDC_TAB)))
                    EndDialog(hwndDlg, TRUE);
                break;
            case IDCANCEL:
                EndDialog(hwndDlg, FALSE);
                break;
            }
            // The above assumes that this is a modal dialog. If it is a modeless
            // don't use EndDialog, use DestroyWindow instead.
        }
        break;
        }
    }
    ... more ...

At the end of the dialog procedure, a call to SftTabs_HandleDialogMessage should be made to allow SftTabs/DLL to process any messages. Without this call, SftTabs/DLL may not be able to provide the tabbed dialog handling, such as TAB and ESCAPE key processing, etc. Please note that the SftTabs_TransparentControls function is not used in a main dialog, only in tab pages.

    if (SftTabs_HandleDialogMessage(hwndDlg, msg, wParam, lParam))
        return TRUE;

    return FALSE;
}

The SFTTABS_TAB structures used to define each tab in the tab control also define a callback routine of type SFTTABS_TABCALLBACK. This callback routine is responsible for creating and destroying the dialog which represents the page. SftTabs/DLL will hide and disable a dialog, but it is up to this callback routine to create and destroy the dialog. The following tab definition is repeated here so it can be shown next to the callback function:

static const SFTTABS_TAB Tab2 = {   /*&Third */
    SFTTABS_NOCOLOR, RGB(128,128,0),     /* background, foreground color */
    SFTTABS_NOCOLOR, RGB(128,128,0),     /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* Bitmap, Icon */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page3_Callback,              /* user supplied tab callback */
    NULL,                                /* reserved */
    SFTTABS_NOCOLOR,                     /* Flyby foreground color */
    SFTTABS_NOCOLOR,                     /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    SFTTABS_NOCOLOR,                     /* gradient fill background color */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill background color, active tab */
    SFTTABS_NOCOLOR,
    SFTTABS_NOCOLOR,                     /* gradient fill client area color */
    SFTTABS_NOCOLOR,
};
HWND CALLBACK Page3_Callback(BOOL fCreate, HWND hwndOwner, HWND hwndPage, HWND hwndTab)
{
    if (fCreate) {                      // creating a new page
        if (hwndPage) {
            // already created, we could do some initialization here.
            // this will be called every time the page becomes active
            // The WM_CREATE/WM_INITDIALOG/WM_DESTROY messages are also sent to
            // the page and could be used to determine activation/deactivation.
            // of the page.

            // optional, set the main window's title to the window title defined for this page
            SftTabs_CopyWindowTitle(hwndPage, hwndOwner);
            return NULL;
        } else {
            // Create the page.
            // You can create and initialize any type of window here, not just dialogs.
            // Use CreateWindow to create other windows. Don't specify WS_VISIBLE, but
            // make sure you use WS_TABSTOP.
            // When creating a non-dialog window, make sure to call SftTabs_SetPageActive
            // after the page has been created.
            HWND hwnd = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_PAGE3), hwndOwner, (DLGPROC)Page3_DialogProc,
                    (LPARAM)hwndTab);// pass tab control as data
            // optional, set the main window's title to the window title defined for this page
            SftTabs_CopyWindowTitle(hwnd, hwndOwner);
            return hwnd;
        }
    } else {                            // destroying page
        // We'll always destroy this page (to save resources)
        DestroyWindow(hwndPage);
        return NULL;
    }
}

This callback routine is called by SftTabs/DLL to perform certain functions based on the parameters passed.

The callback routine creates the dialog (page) using CreateDialogParam. The window handle of the tab control is passed as the last parameter to CreateDialogParam. The WM_INITDIALOG processing of the page's dialog procedure needs access to the tab control's window handle for the call to SftTabs_SetPageActive. This is a convenient way to pass it to the dialog procedure. Of course, it could also be passed using other mechanisms.

The tab page dialog resource must be defined as a child window with the WS_CHILD window style. It cannot be defined as a popup or overlapped window (WS_POPUP or WS_OVERLAPPED)

Creating a Page Dialog Procedure

Once the page is created, all Windows messages for the page are sent to the page's dialog procedure. Any messages that the page doesn't handle should be passed to SftTabs/DLL by using a call to SftTabs_HandleDialogMessage.

BOOL CALLBACK Page3_DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lResult;

    switch (msg) {
    case WM_INITDIALOG:
        SetWindowText(GetDlgItem(hwndDlg, IDC_P3_EDIT1), TEXT("Type Here"));
        // initialize page
        SftTabs_SetPageActive(hwndDlg, (HWND) lParam, NULL);
        return !SftTabs_IsRegisteredDialog(GetParent(hwndDlg));
    }

    if (SftTabs_HandleDialogMessage(hwndDlg, msg, wParam, lParam))
        return TRUE;
    if (SftTabs_TransparentControls(hwndDlg, NULL, &msg, &wParam, &lParam, &lResult, 0, 0))
        return (BOOL) lResult;

    return FALSE;
}

When the page is initialized and handles the WM_INITDIALOG message, it must call SftTabs_SetPageActive to notify SftTabs/DLL that this page is the active page. SftTabs/DLL will at this time resize the page to fit into the tab control's client area, override certain incompatible window styles and make the page visible.

When the very first page is initialized, the focus should be on the first control in that page. Otherwise, when other pages have been active before, the focus should remain on the control which currently has the focus (usually the tab control). The return value of the WM_INITDIALOG message handler determines the focus handling. GetParent below returns the window handle of the page's parent window, which should be the tabbed dialog. This window handle can be tested to find out if the dialog has been registered with SftTabs/DLL. When the very first page is activating, the tabbed dialog has not yet been registered, SftTabs_IsRegisteredDialog would return FALSE and the dialog procedure would subsequently return TRUE, allowing the focus to be set to the first control. Otherwise, if this is not the very first page, FALSE would be returned, and the focus would remain on the control that currently has the focus.

return !SftTabs_IsRegisteredDialog(GetParent(hwndDlg));

Implementing Tabbed Windows

A tabbed window is created just like a regular window. A tabbed window has at least one tab control as its child window (with or without a client area). Each tab has an attached page (although during development of a tabbed dialog, a tab doesn't require an attached page). As the user switches between tabs, the appropriate page is created, displayed and destroyed.

Creating a Tabbed Window Window Procedure

The following sample code (from \Program Files (x86)\Softelvdm\SftTabs DLL 6.5\Samples\C\Window\Window.c) shows a typical window procedure used for a tabbed window. Most of the code has been created using the SftTabs/DLL Wizard and then copied into the application.

/**********************************************************************/
/*                            Frame Window                            */
/**********************************************************************/

/*- Tab Control Initialization Data --------------------------------------------*/

static const SFTTABS_CONTROL CtlInit = {
    SFTTABSSTYLE_MODERN_II,              /* tab style */
    1,                                   /* number of rows */
    0,                                   /* number of tabs per row (if fFixed) */
    0,                                   /* width of left margin */
    0,                                   /* width of right margin */
    FALSE,                               /* same width for all tabs */
    FALSE,                               /* Client area wanted */
    FALSE,                               /* allow multiline label text */
    TRUE,                                /* use with dialog */
    FALSE,                               /* use specified background color only for text */
    TRUE,                                /* scrollable tabs */
    FALSE,                               /* hide scroll buttons */
    TRUE,                                /* bold font for active tab wanted */
    FALSE,                               /* fill rows completely */
    NULL,                                /* scroll button bitmap */
    NULL,                                /* Dialog data associated with active tab */
    NULL,                                /* Dialog window handle associated with active tab */
    NULL,                                /* Frame, used as client area */
    TRUE,                                /* Tooltips wanted */
    FALSE,                               /* drop text if it doesn't fit */
    FALSE,                               /* conditional scroll buttons */
    BMBUTTONSTYLE_THEME_SCROLL,          /* scroll button style */
    FALSE,                               /* display ... if truncated */
    TRUE,                                /* Flyby highlighting */
    FALSE,                               /* use client area colors in partially obscured frames */
    TRUE,                                /* scroll buttons on left side */
    0,                                   /* row indentation */
    FALSE,                               /* don't show truncated pattern for clipped tab */
    TRUE,                                /* full size scroll buttons */
    FALSE,                               /* use themes on Windows XP */
    FALSE,                               /* use exact window region */
    FALSE,                               /* always show prefix _ */
    0,0,0,0,                             /* animation values */
    NULL,                                /* disabled button bitmap */
    TRUE,                                /* focus rectangle if the control has i/p focus */
    FALSE,                               /* TRUE if Close button wanted */
    FALSE,                               /* TRUE if Close button disabled */
    FALSE,                               /* TRUE if WM_CLOSE message wanted */
    FALSE,                               /* TRUE if Minimize, Restore, Close buttons are full size */
    SFTTABS_BUTTON_NEAR,                 /* scroll button alignment */
    SFTTABS_BUTTON_NEAR,                 /* Minimize, Restore, Close button alignment */
    FALSE,                               /* TRUE if Minimize button wanted */
    FALSE,                               /* TRUE if Minimize button disabled */
    FALSE,                               /* TRUE if Restore button wanted */
    FALSE,                               /* TRUE if Restore button disabled */
    NULL,                                /* Close, Minimize, Restore button bitmap */
    NULL,                                /* Close, Minimize, Restore disabled button bitmap */
    TEXT("Scroll Left"),                 /* scroll left button tooltip */
    TEXT("Scroll Right"),                /* scroll button tooltip */
    TEXT(""),                            /* Close button tooltip */
    TEXT(""),                            /* Minimize button tooltip */
    TEXT(""),                            /* Restore button tooltip */
    0,                                   /* custom modifications */
    0,                                   /* forced height/width depending on tab style - 0 to ignore */
    FALSE,                               /* switch tabs on button release (or down if FALSE) */
    FALSE,                               /* Rendering compatible with pre-6.0 */
    FALSE,                               /* don't display clientarea border - select styles only */
};

static const SFTTABS_TAB Tab0 = {   /*&Listbox */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page1_Callback,  /* user supplied tab callback */
    NULL,                                /* reserved */
    RGB(0,0,255),                        /* Flyby foreground color */
    RGB(129,169,226),                    /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    RGB(222,236,254),                    /* gradient fill background color */
    RGB(129,169,226),
    RGB(222,236,254),                    /* gradient fill background color, active tab */
    RGB(129,169,226),
    RGB(129,169,226),                    /* gradient fill client area color */
    RGB(222,236,254),
};
static const SFTTABS_TAB Tab1 = {   /*&Edit Control */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page2_Callback,  /* user supplied tab callback */
    NULL,                                /* reserved */
    RGB(0,0,255),                        /* Flyby foreground color */
    RGB(129,169,226),                    /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    RGB(222,236,254),                    /* gradient fill background color */
    RGB(129,169,226),
    RGB(222,236,254),                    /* gradient fill background color, active tab */
    RGB(129,169,226),
    RGB(129,169,226),                    /* gradient fill client area color */
    RGB(222,236,254),
};
static const SFTTABS_TAB Tab2 = {   /*&Dialog */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* location */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page3_Callback,  /* user supplied tab callback */
    NULL,                                /* reserved */
    RGB(0,0,255),                        /* Flyby foreground color */
    RGB(129,169,226),                    /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    RGB(222,236,254),                    /* gradient fill background color */
    RGB(129,169,226),
    RGB(222,236,254),                    /* gradient fill background color, active tab */
    RGB(129,169,226),
    RGB(129,169,226),                    /* gradient fill client area color */
    RGB(222,236,254),
};

The window procedure shown here initializes the tab control in its WM_CREATE message handler.

LRESULT CALLBACK Frame_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    ... other source code removed
    case WM_CREATE: {
        int index;

        /* This static window is just a filler window above the tab control */
        /* We only need it to get the right background color above the tab control */
        pfrm->hwndStatic = CreateWindow(/* Create a static window */
            TEXT("STATIC"),             /* Window Class */
            TEXT(""),                   /* Window Title (not used) */
            WS_CHILD|WS_VISIBLE,        /* Window Style */
            0, 0,                       /* x, y */
            0, 0,                       /* cx, cy */
            hwnd,                       /* Parent Window */
            NULL,                       /* control ID */
            g_hInst,                    /* Application Instance */
            NULL);                      /* creation data */
        if (pfrm->hwndStatic == NULL)   /* create failed */
            return -1;

        /* Create the tab control */
        pfrm->hwndTab = CreateWindow(   /* Create the tab control */
            TEXT(SFTTABS_CLASS),        /* Window Class */
            TEXT(""),                   /* Window Title (not used) */
            WS_CHILD|WS_VISIBLE|        /* Window Style */
            WS_CLIPCHILDREN|WS_TABSTOP|
            SFTTABSSTYLE_STANDARD,
            0, 0,                       /* x, y */
            0, 0,                       /* cx, cy */
            hwnd,                       /* Parent Window */
            (HMENU) IDC_TAB,            /* Tab control ID */
            g_hInst,                    /* Application Instance */
            NULL);                      /* creation data */
        if (pfrm->hwndTab == NULL)      /* create failed */
            return -1;

        /* Create a frame window. This frame window will be used by SftTabs/DLL */
        /* to resize the pages that are attached to the tabs */
        pfrm->hwndFrame = CreateWindow( /* Create a static window */
            TEXT("STATIC"),             /* Window Class */
            TEXT(""),                   /* Window Title (not used) */
            WS_CHILD,                   /* Window Style NOTE: IT'S NOT VISIBLE */
            0, 0,                       /* x, y */
            0, 0,                       /* cx, cy */
            hwnd,                       /* Parent Window */
            NULL,                       /* control ID */
            g_hInst,                    /* Application Instance */
            NULL);                      /* creation data */
        if (pfrm->hwndFrame == NULL)   /* create failed */
            return -1;

        /* Initialization is faster if we set redraw off */
        SendMessage(pfrm->hwndTab, WM_SETREDRAW, (WPARAM)FALSE, 0);

        /* We are using new features */
        SftTabs_SetVersion(pfrm->hwndTab, SFTTABS_6_5);

        index = SftTabs_AddTab(pfrm->hwndTab, TEXT("&Listbox"));
        SftTabs_SetToolTip(pfrm->hwndTab, index, TEXT("A standard listbox is attached to this tab"));
        SftTabs_SetTabInfo(pfrm->hwndTab, index, &Tab0);

        index = SftTabs_AddTab(pfrm->hwndTab, TEXT("&Edit Control"));
        SftTabs_SetToolTip(pfrm->hwndTab, index, TEXT("A standard edit control is attached to this tab"));
        SftTabs_SetTabInfo(pfrm->hwndTab, index, &Tab1);

        index = SftTabs_AddTab(pfrm->hwndTab, TEXT("&Dialog"));
        SftTabs_SetToolTip(pfrm->hwndTab, index, TEXT("A dialog is attached to this tab"));
        SftTabs_SetTabInfo(pfrm->hwndTab, index, &Tab2);

        SftTabs_SetControlInfo(pfrm->hwndTab, &CtlInit);

        SftTabs_SetCurrentTab(pfrm->hwndTab, 0);

        // Make sure to turn redraw back on
        SendMessage(pfrm->hwndTab, WM_SETREDRAW, (WPARAM)TRUE, 0);
        InvalidateRect(pfrm->hwndTab, NULL, TRUE);

        // Activate current page. Note the frame window is supplied in this example.
        // if your tab control has a client area (see fClientArea), you don't need a
        // frame window
        SftTabs_ActivatePage(hwnd, pfrm->hwndTab, pfrm->hwndFrame, TRUE);

        // Mark the window as a main, tabbed windows (so accel. keys work) by registering it.
        // Register the window AFTER activating the current page
        SftTabs_RegisterWindow(hwnd);

        return 0L;
     }
    ... other source code removed

The call to SftTabs_ActivatePage makes the current page active. The SftTabs_RegisterWindow call registers the window for special tabbed window handling by SftTabs/DLL. After registering the window, keyboard accelerator keys (Alt-x) handling will be performed by SftTabs/DLL.

The following WM_SIZE message handler resizes the tabbed window's child windows:

    case WM_SIZE: {
        RECT rect;

        // resize all child windows

        // get frame window dimension
        GetClientRect(hwnd, &rect);

        // calculate position of tab control
        if (pfrm->hwndTab) {
            SFTTABS_CONTROL Ctl;
            // Get tab control info
            SftTabs_GetControlInfo(pfrm->hwndTab, &Ctl);
            // Ctl.naturalSize has best height for this tab control

            rect.top += 5+Ctl.naturalSize;
        }

        // reposition static control which serves as a filler window above the tab control
        if (pfrm->hwndStatic)
            MoveWindow(pfrm->hwndStatic, rect.left, 0, rect.right-rect.left, 5, TRUE);

        // now reposition tab control
        if (pfrm->hwndTab)
            MoveWindow(pfrm->hwndTab, 0, 5, rect.right-rect.left, rect.top-5, TRUE);

        // reposition the frame window
        if (pfrm->hwndFrame)
            MoveWindow(pfrm->hwndFrame, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
        // now that the frame window has the right size, resize all pages
        if (pfrm->hwndTab)
            SftTabs_ResizePages(pfrm->hwndTab);

        return 0L;
     }

The following WM_DESTROY message handler shows the required cleanup calls and also unregisters the dialog from SftTabs/DLL:

    case WM_DESTROY:
        // Unregister, or the window properties used won't be removed
        SftTabs_UnregisterWindow(hwnd);
        // destroy all pages (BEFORE destroying tab control)
        SftTabs_Destroy(hwnd, pfrm->hwndTab);

        if (pfrm->hwndStatic)
            DestroyWindow(pfrm->hwndStatic);
        if (pfrm->hwndFrame)
            DestroyWindow(pfrm->hwndFrame);
        if (pfrm->hwndTab)
            DestroyWindow(pfrm->hwndTab);
        break;

The following WM_COMMAND message handler responds to notifications sent to the tabbed window by the tab control. When the user attempts to switch to another page by clicking a tab using the left mouse button or by using the keyboard, a SFTTABSN_SWITCHING notification is sent to the tabbed window. The window then calls SftTabs_DeactivatePage to test if the active page can be left. This call to SftTabs_DeactivatePage results in a WM_QUERYENDSESSION message being sent to the page's dialog or window procedure. Based on the window procedure's response, the page may or may not be deactivated. By handling the WM_QUERYENDSESSION message, a page can prevent a user from switching away from a tab.

If a new tab has become active, the tabbed window receives a SFTTABSN_SWITCHED notification, so the associated page can be made active by the call to SftTabs_ActivatePage.

    case WM_COMMAND: {
        HWND hwndCtl = (HWND) lParam;
        int id = LOWORD(wParam);
        int code = HIWORD(wParam);
        if (hwndCtl) {

            switch (id) {
            case IDC_TAB:
                switch (code) {
                case SFTTABSN_SWITCHING:// we're about to switch away from
                    // the current page.  If you need to know what the new
                    // page will be use SftTabs_GetNextTab(hwndCtl).
                    if (!SftTabs_DeactivatePage(hwnd, hwndCtl))
                        // couldn't deactivate current page, so don't switch
                        SendMessage(hwndCtl, WM_CANCELMODE, 0, 0);
                    break;
                case SFTTABSN_SWITCHED:// we switched to a new page
                    SftTabs_ActivatePage(hwnd, hwndCtl, NULL, FALSE);
                    break;
                }
                break;

            case IDOK:
            case IDCANCEL:
                if (code == BN_CLICKED)
                    SendMessage(hwnd, WM_COMMAND, id, 0);
                break;
            }

        } else {
            switch (id) {
            case IDM_EXIT:
                // The currently active page will be called with a
                // WM_QUERYENDSESSION/SftTabs_GetEndPageMessage message to determine
                // whether it can be closed
                if (SftTabs_ClosePossible(hwnd, GetDlgItem(hwnd, IDC_TAB))) {
                    DestroyWindow(hwnd);
                    PostQuitMessage(0);
                }
                break;
            }
        }
        break;

At the end of the window procedure, a call to SftTabs_HandleDialogMessage should be made to allow SftTabs/DLL to process any messages. Without this call, SftTabs/DLL may not be able to provide the tabbed dialog handling.

    ... other source code removed

    // Call SftTabs/DLL to let it handle some messages (mostly for keyboard accel. keys)
    {
        LRESULT lRes;
        if (SftTabs_HandleWindowMessage(hwnd, msg, wParam, lParam, &lRes))
            return lRes;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

The SFTTABS_TAB structures used to define each tab in the tab control also define a callback routine of type SFTTABS_TABCALLBACK. This callback routine is responsible for creating and destroying the window which represents the page. SftTabs/DLL will hide and disable a window, but it is up to this callback routine to create and destroy the window. The following tab definition is repeated here so it can be shown next to the callback function:

static const SFTTABS_TAB Tab1 = {   /*&Edit Control */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color */
    RGB(129,169,226), RGB(0,0,0),        /* background, foreground color (when selected) */
    { SFTTABS_GRAPH_NONE, 0 },           /* Bitmap, Icon */
    TRUE,                                /* enabled/disabled */
    0,                                   /* userdata */
    (SFTTABS_DWORD_PTR) Page2_Callback,  /* user supplied tab callback */
    NULL,                                /* reserved */
    RGB(0,0,255),                        /* Flyby foreground color */
    RGB(129,169,226),                    /* Client area background color */
    0,0,0,0,                             /* animation values */
    NULL,                                /* tab-specific ImageList handle*/
    FALSE,                               /* hidden tab */
    RGB(222,236,254),                    /* gradient fill background color */
    RGB(129,169,226),
    RGB(222,236,254),                    /* gradient fill background color, active tab */
    RGB(129,169,226),
    RGB(129,169,226),                    /* gradient fill client area color */
    RGB(222,236,254),
};
HWND CALLBACK Page2_Callback(BOOL fCreate, HWND hwndOwner, HWND hwndPage, HWND hwndTab)
{
    if (fCreate) {                      // creating a new page
        if (hwndPage) {
            // already created, we could do some initialization here.
            // this will be called every time the page becomes active.
            // The WM_SHOWWINDOW message is also sent to the page and could
            // be used to determine activation/deactivation of the page.
            SetWindowText(hwndPage, TEXT("Thank you for coming back."));
            return NULL;                // return NULL, ignored
        } else {
            // create the window
            HWND hwnd;

            // Create the edit control
            // You can create and initialize any type of window here, not just dialogs.
            // Use CreateWindow to create other windows. Don't specify WS_VISIBLE, but
            // make sure you use WS_TABSTOP.
            hwnd = CreateWindow(  /* Create the list box */
                "EDIT",                 /* Window Class */
                TEXT(""),               /* Window Title (not used) */
                WS_CHILD|               /* Window Style */
                WS_TABSTOP|ES_MULTILINE|ES_WANTRETURN,
                0, 0,                   /* x, y */
                0, 0,                   /* cx, cy */
                hwndOwner,              /* Parent Window */
                (HMENU) IDC_EDIT,       /* control ID */
                g_hInst,                /* Application Instance */
                NULL);                  /* creation data */
            if (hwnd == NULL)           /* create failed */
                return NULL;

            SendMessage(hwnd, WM_SETFONT, SendMessage(hwndTab, WM_GETFONT, 0, 0L), FALSE);

            SetWindowText(hwnd, TEXT("This is an edit control.\r\nClick a tab or use Alt-xxx to switch to another tab. "
                                            " Make sure to look at the \"Dialog\" tab also."));
            SftTabs_SetPageActive(hwnd, hwndTab, NULL);
            return hwnd;
        }

    } else {                            // destroying page
        if (hwndOwner)                  // - because we're switching away
            return hwndPage;            //   keep the window handle, don't destroy it
        else {                          // - because we're closing the main dialog
            DestroyWindow(hwndPage);
            return NULL;
        }
    }
}

This callback routine is called by SftTabs/DLL to perform certain functions based on the parameters passed.

The callback routine creates the window (page) using CreateWindow.


Last Updated 08/13/2020 - (email)
© 2024 Softel vdm, Inc.