탭 대화 상자를 만드는 방법
이 섹션의 예에서는 탭을 사용하여 여러 페이지의 컨트롤을 제공하는 대화 상자를 만드는 방법을 보여 줍니다. 기본 대화 상자는 모달 대화 상자입니다. 컨트롤의 각 페이지는 WS_CHILD 스타일이 있는 대화 상자 템플릿으로 정의됩니다. 탭을 선택하면 들어오는 페이지에 대한 모덜리스 대화 상자가 만들어지고 나가는 페이지에 대한 대화 상자가 제거됩니다.
참고
대부분의 경우 속성 시트를 사용하여 여러 페이지 대화 상자를 보다 쉽게 구현할 수 있습니다. 속성 시트에 대한 자세한 내용은 속성 시트 정보를 참조하세요.
기본 대화 상자의 템플릿은 단순히 두 개의 단추 컨트롤을 정의합니다. WM_INITDIALOG 메시지를 처리할 때 대화 상자 프로시저는 탭 컨트롤을 만들고 각 자식 대화 상자에 대한 대화 상자 템플릿 리소스를 로드합니다.
알아야 하는 작업
기술
필수 구성 요소
- C/C++
- Windows 사용자 인터페이스 프로그래밍
지침
탭 대화 상자 만들기
정보는 DLGHDR이라는 애플리케이션 정의 구조체에 저장됩니다. 이 구조체에 대한 포인터는 SetWindowLong 함수를 사용하여 대화 상자 창과 연결됩니다. 구조체는 다음과 같이 애플리케이션의 헤더 파일에 정의됩니다.
#define C_PAGES 3
typedef struct tag_dlghdr {
HWND hwndTab; // tab control
HWND hwndDisplay; // current child dialog box
RECT rcDisplay; // display rectangle for the tab control
DLGTEMPLATEEX *apRes[C_PAGES];
} DLGHDR;
다음 함수는 기본 대화 상자에 대한 WM_INITDIALOG 메시지를 처리합니다. 이 함수는 DLGHDR
구조체를 할당하고, 자식 대화 상자에 대한 대화 상자 템플릿 리소스를 로드하고, 탭 컨트롤을 만듭니다.
각 자식 대화 상자의 크기는 DLGTEMPLATEEX 구조체로 지정됩니다. 이 함수는 각 대화 상자의 크기를 검사하고 TCM_ADJUSTRECT 메시지에 대한 매크로를 사용하여 탭 컨트롤의 적절한 크기를 계산합니다. 그런 다음 대화 상자의 크기를 조정하고 그에 따라 두 개의 단추를 배치합니다. 이 예는 TabCtrl_AdjustRect 매크로를 사용하여 TCM_ADJUSTRECT를 보냅니다.
// Handles the WM_INITDIALOG message for a dialog box that contains
// a tab control used to select among three child dialog boxes.
// Returns a result code.
// hwndDlg - handle of the dialog box.
//
HRESULT OnTabbedDialogInit(HWND hwndDlg)
{
INITCOMMONCONTROLSEX iccex;
DWORD dwDlgBase = GetDialogBaseUnits();
int cxMargin = LOWORD(dwDlgBase) / 4;
int cyMargin = HIWORD(dwDlgBase) / 8;
TCITEM tie;
RECT rcTab;
HWND hwndButton;
RECT rcButton;
int i;
// Initialize common controls.
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&iccex);
// Allocate memory for the DLGHDR structure. Remember to
// free this memory before the dialog box is destroyed.
DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR));
// Save a pointer to the DLGHDR structure in the window
// data of the dialog box.
SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pHdr);
// Create the tab control. Note that g_hInst is a global
// instance handle.
pHdr->hwndTab = CreateWindow(
WC_TABCONTROL, L"",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, 100, 100,
hwndDlg, NULL, g_hInst, NULL
);
if (pHdr->hwndTab == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// Add a tab for each of the three child dialog boxes.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = L"First";
TabCtrl_InsertItem(pHdr->hwndTab, 0, &tie);
tie.pszText = L"Second";
TabCtrl_InsertItem(pHdr->hwndTab, 1, &tie);
tie.pszText = L"Third";
TabCtrl_InsertItem(pHdr->hwndTab, 2, &tie);
// Lock the resources for the three child dialog boxes.
pHdr->apRes[0] = DoLockDlgRes(MAKEINTRESOURCE(IDD_FIRSTDLG));
pHdr->apRes[1] = DoLockDlgRes(MAKEINTRESOURCE(IDD_SECONDDLG));
pHdr->apRes[2] = DoLockDlgRes(MAKEINTRESOURCE(IDD_THIRDDLG));
// Determine a bounding rectangle that is large enough to
// contain the largest child dialog box.
SetRectEmpty(&rcTab);
for (i = 0; i < C_PAGES; i++)
{
if (pHdr->apRes[i]->cx > rcTab.right)
rcTab.right = pHdr->apRes[i]->cx;
if (pHdr->apRes[i]->cy > rcTab.bottom)
rcTab.bottom = pHdr->apRes[i]->cy;
}
// Map the rectangle from dialog box units to pixels.
MapDialogRect(hwndDlg, &rcTab);
// Calculate how large to make the tab control, so
// the display area can accommodate all the child dialog boxes.
TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab);
OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top);
// Calculate the display rectangle.
CopyRect(&pHdr->rcDisplay, &rcTab);
TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay);
// Set the size and position of the tab control, buttons,
// and dialog box.
SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top,
rcTab.right - rcTab.left, rcTab.bottom - rcTab.top,
SWP_NOZORDER);
// Move the first button below the tab control.
hwndButton = GetDlgItem(hwndDlg, IDB_CLOSE);
SetWindowPos(hwndButton, NULL,
rcTab.left, rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
// Determine the size of the button.
GetWindowRect(hwndButton, &rcButton);
rcButton.right -= rcButton.left;
rcButton.bottom -= rcButton.top;
// Move the second button to the right of the first.
hwndButton = GetDlgItem(hwndDlg, IDB_TEST);
SetWindowPos(hwndButton, NULL,
rcTab.left + rcButton.right + cxMargin,
rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
// Size the dialog box.
SetWindowPos(hwndDlg, NULL, 0, 0,
rcTab.right + cyMargin + (2 * GetSystemMetrics(SM_CXDLGFRAME)),
rcTab.bottom + rcButton.bottom + (2 * cyMargin)
+ (2 * GetSystemMetrics(SM_CYDLGFRAME))
+ GetSystemMetrics(SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER);
// Simulate selection of the first item.
OnSelChanged(hwndDlg);
return S_OK;
}
// Loads and locks a dialog box template resource.
// Returns the address of the locked dialog box template resource.
// lpszResName - name of the resource.
//
DLGTEMPLATEEX* DoLockDlgRes(LPCTSTR lpszResName)
{
HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG);
// Note that g_hInst is the global instance handle
HGLOBAL hglb = LoadResource(g_hInst, hrsrc);
return (DLGTEMPLATEEX *) LockResource(hglb);
}
다음 함수는 기본 대화 상자에 대한 TCN_SELCHANGE 알림 코드를 처리합니다. 이 함수는 나가는 페이지의 대화 상자가 있는 경우 해당 대화 상자를 제거합니다. 그런 다음 CreateDialogIndirect 함수를 사용하여 들어오는 페이지에 대한 모덜리스 대화 상자를 만듭니다.
// Processes the TCN_SELCHANGE notification.
// hwndDlg - handle to the parent dialog box.
//
VOID OnSelChanged(HWND hwndDlg)
{
// Get the dialog header data.
DLGHDR *pHdr = (DLGHDR *) GetWindowLongPtr(
hwndDlg, GWLP_USERDATA);
// Get the index of the selected tab.
int iSel = TabCtrl_GetCurSel(pHdr->hwndTab);
// Destroy the current child dialog box, if any.
if (pHdr->hwndDisplay != NULL)
DestroyWindow(pHdr->hwndDisplay);
// Create the new child dialog box. Note that g_hInst is the
// global instance handle.
pHdr->hwndDisplay = CreateDialogIndirect(g_hInst,
(DLGTEMPLATE *)pHdr->apRes[iSel], hwndDlg, ChildDialogProc);
return;
}
다음 함수는 각 자식 대화 상자에 대한 WM_INITDIALOG 메시지를 처리합니다. CreateDialogIndirect 함수를 사용하여 만들어진 대화 상자의 위치를 지정할 수 없습니다. 이 함수는 SetWindowPos 함수를 사용하여 탭 컨트롤의 표시 영역 내에 자식 대화 상자를 배치합니다.
// Positions the child dialog box to occupy the display area of the
// tab control.
// hwndDlg - handle of the dialog box.
//
VOID WINAPI OnChildDialogInit(HWND hwndDlg)
{
HWND hwndParent = GetParent(hwndDlg);
DLGHDR *pHdr = (DLGHDR *) GetWindowLongPtr(
hwndParent, GWLP_USERDATA);
SetWindowPos(hwndDlg, NULL, pHdr->rcDisplay.left,
pHdr->rcDisplay.top,//-2,
(pHdr->rcDisplay.right - pHdr->rcDisplay.left),
(pHdr->rcDisplay.bottom - pHdr->rcDisplay.top),
SWP_SHOWWINDOW);
return;
}
관련 항목