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>
:
#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:
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:
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;
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 )
{
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 );
}
}
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;
}
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:
case WM_CREATE:
{
EnableThemeDialogTexture( hWnd, ETDT_ENABLETAB );
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" };
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 );
}
TabCtrl_SetCurSel( hwndTab, 0 );
ShowTabPage( hwndTab, TRUE );
}
return 0L;
case WM_NOTIFY: {
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: {
RECT rcClient = {0};
GetClientRect( hWnd, &rcClient );
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 );
ResizeAllTabPages( GetDlgItem( hWnd, 3000 ) );
InvalidateRect( hWnd, NULL, TRUE );
}
return 0L;
5.) It is time for cleanup. Add the following code into
WM_DESTROY
handler:
case WM_DESTROY:
DestroyPages( GetDlgItem( hWnd, IDC_TAB ) ); 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.