Click here to Skip to main content
15,881,173 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
I have dialog box which should act as tab page. My application uses Visual Styles.

I ran into background painting problem when tried to properly center child dialog box into tab's display area. Asking for help, I got an advice to resize the child dialog box to fit the tab control's entire display area. After doing that, the painting problem seemed fixed, but now I do not know how to center dialog's child controls.

I have tried handling WM_SIZE but got nowhere. I have searched the internet but wasn't able to find a solution ( I am a poor Googler to be honest ).

The best would be for me to post small SSCCE so you can see what mean:

1.) Create default Win32 project in Visual Studio.

2.) Add the following in the stdafx.h, just below #include <windows.h> :
C++
#include <windowsx.h>
#include <commctrl.h>
#include <stdio.h>
#include <Uxtheme.h>

#pragma comment( linker, "/manifestdependency:\"type='win32' \
                         name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
                         processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
                         language='*'\"")

#pragma comment( lib, "comctl32.lib")
#pragma comment( lib, "UxTheme.lib")

3.) Inside ATOM MyRegisterClass(HINSTANCE hInstance) function ( it is generated in .cpp file ), initialize common controls:
C++
// initialize common controls

INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_STANDARD_CLASSES | ICC_TAB_CLASSES;
InitCommonControlsEx(&iccex);


4.) Above the main window's window procedure ( WndProc, also generated in .cpp file ), add the following functions:
C++
//===================== helpers for tab control

BOOL ShowTabPage( HWND hwndTab, BOOL bShow )
{
    TCITEM tciSelected = {0};
    tciSelected.mask = TCIF_PARAM;

    int i = SendMessage( hwndTab, TCM_GETCURSEL, 0, 0 );

    if ( -1 == i )
        return FALSE;

    if( !TabCtrl_GetItem( hwndTab, i, &tciSelected ) )
        return FALSE;

    // loop through pages -> notice that the dialog handle is stored in lParam !
    for( int j = 0; j < (int)TabCtrl_GetItemCount(hwndTab); j++ )
    {
        TCITEM tci = {0};
        tci.mask = TCIF_PARAM;

        if( !TabCtrl_GetItem( hwndTab, j, &tci ) )
            continue;

        BOOL bEqual = ( (HWND)tci.lParam == (HWND)tciSelected.lParam );
            ShowWindow( (HWND)tci.lParam, 
                ( bShow && bEqual ) ? SW_SHOW : SW_HIDE );

        EnableWindow( (HWND)tci.lParam, bShow && bEqual );
    }
    return TRUE;
}

void GetTabDisplayRect( HWND hwndTab, RECT &rcTab )
{
    GetClientRect( hwndTab, &rcTab );

    MapWindowPoints( hwndTab, GetParent(hwndTab), (LPPOINT)(&rcTab), 2 );

    TabCtrl_AdjustRect( hwndTab, FALSE, &rcTab );
}

void ResizeTabPage( HWND hDlg, RECT &rcTab )
{
    SetWindowPos( hDlg, HWND_TOP, 
        rcTab.left, rcTab.top,
        rcTab.right - rcTab.left, 
        rcTab.bottom - rcTab.top, 
        SWP_NOCOPYBITS);
}

void ResizeAllTabPages( HWND hwndTab )
{
    // loop through pages -> notice that the dialog handle is stored in lParam !
    for( int i = 0; i < (int)TabCtrl_GetItemCount(hwndTab); i++ )
    {
        TCITEM tci = {0};
        RECT rcTab = {0};

        GetTabDisplayRect( hwndTab, rcTab );

        tci.mask = TCIF_PARAM;
        if( !TabCtrl_GetItem( hwndTab, i, &tci ) )
            continue;

        ResizeTabPage( (HWND)tci.lParam, rcTab );
    }
}

// destroys child dialog boxes, returns number of failed destroys
int DestroyPages( HWND hwndTab )
{
    int i = 0;
    for( int j = 0; j < (int)TabCtrl_GetItemCount(hwndTab); j++ )
    {
        TCITEM tci = {0};
        tci.mask = TCIF_PARAM;

        if( !TabCtrl_GetItem( hwndTab, j, &tci ) )
        {
            i++;
            continue;
        }

        if ( !DestroyWindow( (HWND)tci.lParam ) )
            i++;
    }
    return i;
}

// Message handler for tab page
INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    default:
        break;
    }
    return (INT_PTR)FALSE;
}

4.) In main window procedure (WndProc), add the following handlers:
C++
case WM_CREATE:
    {
        // this call should paint child dialog boxes properly
        // I have tried adding this into child dialog box
        // but got bad results
        // for details see the following Stack Overflow question:
        // http://stackoverflow.com/questions/27085427/common-controls-are-not-properly-painted-when-i-resize-window
        
        EnableThemeDialogTexture( hWnd, ETDT_ENABLETAB );
        
        // create tab control
        RECT rcClient = {0};
        GetClientRect( hWnd, &rcClient );
        
        HWND hwndTab = CreateWindowEx( 0, WC_TABCONTROL, 
        L"Main tab control", 
        WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 
        10, 10, 
        rcClient.right - rcClient.left - 20,
        rcClient.bottom - rcClient.top - 53, 
        hWnd, 
        (HMENU)3000, 
        ((LPCREATESTRUCT)lParam)->hInstance, 0 );
        
        wchar_t *szTabs[29] = { L"First Tab", L"Second Tab", L"Third tab" };
        
        // add 3 tabs
        for( int i = 0; i < 2; i++ )
        {
            TCITEM tci = {0};
            
            tci.mask = TCIF_TEXT | TCIF_PARAM;
            tci.pszText = szTabs[i];
            
            HWND hDlgPage = CreateDialog( 
            GetModuleHandle(NULL),
            MAKEINTRESOURCE(IDD_DIALOG1), 
            hWnd, 
            (DLGPROC)DlgProc ); 
            
            tci.lParam = (LPARAM)hDlgPage;
            
            TabCtrl_InsertItem( hwndTab, i, &tci );
        }
        
        // select first tab and show it
        TabCtrl_SetCurSel( hwndTab, 0 );
        ShowTabPage( hwndTab, TRUE );
    }
    return 0L;
case WM_NOTIFY:  // handle tab switching
    {
        switch (((LPNMHDR)lParam)->code)
        {
        case TCN_SELCHANGING:
            ShowTabPage( GetDlgItem( hWnd, 3000 ), FALSE );
            return FALSE;
            break;
        case TCN_SELCHANGE:
            ShowTabPage( GetDlgItem( hWnd, 3000 ), TRUE );
            break;
        default:
            break;
        }
    }
    break;
case WM_SIZE:  // here lies the problem!
    {
        RECT rcClient = {0};
        GetClientRect( hWnd, &rcClient );
        
        // resize tab control
        SetWindowPos( GetDlgItem( hWnd, 3000 ), NULL, 
        rcClient.left + 10, 
        rcClient.top + 10, 
        rcClient.right - rcClient.left - 20, 
        rcClient.bottom - rcClient.top - 53,
        SWP_NOZORDER | SWP_NOCOPYBITS );
        
        // resize all child dialog boxes
        ResizeAllTabPages( GetDlgItem( hWnd, 3000 ) );
        
        InvalidateRect( hWnd, NULL, TRUE );
    }
    return 0L;

5.) It is time for cleanup. Add the following code into WM_DESTROY handler:
C++
case WM_DESTROY:
    DestroyPages( GetDlgItem( hWnd, IDC_TAB ) );  // our cleanup!
    PostQuitMessage(0);
    break;

6.) The last thing left is to create dialog box and add some controls into it. Make sure it has ID IDD_DIALOG1 and set the following styles:

- Border : none
- Control : true
- Control parent : true
- Style : child
- System menu : false

7.) Run the application and you will see that dialog box has proper background but child controls are not properly moved to preserve original arrangement. The same thing happens when you resize the main window.

QUESTION:

How to rearrange dialog boxes child controls so they preserve original proportions and arrangement?

Thank you. Best regards.
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900