本节中的示例将创建一个对话框,该对话框使用选项卡来提供多页控件。主对话框是一个模态对话框。控件的每一页由指定WS_CHILD样式的对话框模板定义。当选择选项卡时,该示例为传入页面创建无模式对话框,并销毁出站页面的对话框。
注意在许多情况下,您可以通过使用属性表更容易地实现多页面对话框。有关属性表的详细信息,请参阅物业单.
主对话框的模板简单地定义了两个按钮控件。处理WM_INITDIALOG消息时,对话框过程将创建一个选项卡控件,并加载每个子对话框的对话框模板资源。
信息保存在一个名为DLGHDR的应用程序定义的结构中。指向此结构的指针通过使用SetWindowLong功能与对话框窗口相关联。结构在应用程序的头文件中定义如下。
#define C_PAGES 3
typedef struct tag_dlghdr {
HWND hwndTab; //标签控件
HWND hwndDisplay; //当前子对话框
RECT rcDisplay; //显示标签控件的矩形
DLGTEMPLATE *apRes[C_PAGES];
} DLGHDR;
以下功能处理主对话框的WM_INITDIALOG消息。该函数分配DLGHDR结构,加载子对话框的对话框模板资源,并创建选项卡控件。
每个子对话框的大小由DLGTEMPLATE结构指定。该函数检查每个对话框的大小,并使用TCM_ADJUSTRECT消息的宏来计算标签控件的适当大小。然后它对齐对话框并相应地定位两个按钮。此示例使用TabCtrl_AdjustRect宏发送TCM_ADJUSTRECT。
VOID WINAPI OnTabbedDialogInit(HWND hwndDlg)
{
DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR));
DWORD dwDlgBase = GetDialogBaseUnits();
int cxMargin = LOWORD(dwDlgBase) / 4;
int cyMargin = HIWORD(dwDlgBase) / 8;
TC_ITEM tie;
RECT rcTab;
HWND hwndButton;
RECT rcButton;
int i;
//保存指向DLGHDR结构的指针。
SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr);
//创建标签控件。
InitCommonControls();
pHdr->hwndTab = CreateWindow(
WC_TABCONTROL, "",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, 100, 100,
hwndDlg,NULL,g_hinst,NULL
);
if (pHdr->hwndTab == NULL) {
//处理错误
}
//为三个子对话框中的每一个添加一个选项卡。
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = "First";
TabCtrl_InsertItem(pHdr->hwndTab, 0, &tie);
tie.pszText = "Second";
TabCtrl_InsertItem(pHdr->hwndTab, 1, &tie);
tie.pszText = "Third";
TabCtrl_InsertItem(pHdr->hwndTab, 2, &tie);
//锁定三个子对话框的资源。
pHdr->apRes[0] = DoLockDlgRes(MAKEINTRESOURCE(DLG_FIRST));
pHdr->apRes[1] = DoLockDlgRes(MAKEINTRESOURCE(DLG_SECOND));
pHdr->apRes[2] = DoLockDlgRes(MAKEINTRESOURCE(DLG_THIRD));
//确定所有子对话框的边界矩形。
SetRectEmpty(&rcTab);
for (i = 0; i < C_PAGES; i++) {
如果(pHdr-后{989 796 004} {989 796 010} - > > rcTab.right CX)
rcTab.right = pHdr->apRes[i]->cx;
如果(pHdr- {989 796 004} {后989 796 012} - > > CY rcTab.bottom)
rcTab.bottom = pHdr->apRes[i]->cy;
}
rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4;
rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8;
//计算制表符控制的大小,所以
//显示区域可以容纳所有的子对话框。
TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab);
OffsetRect(& rcTab,cxMargin - rcTab.left,
cyMargin - rcTab.top);
//计算显示矩形。
CopyRect(&pHdr->rcDisplay, &rcTab);
TabCtrl_AdjustRect(pHdr- > hwndTab,FALSE,& pHdr- > rcDisplay);
//设置标签控件的大小和位置,按钮,
//和对话框。
SetWindowPos(pHdr- > hwndTab,NULL,rcTab.left,rcTab.top,
rcTab.right - rcTab.left,rcTab.bottom - rcTab.top,
SWP_NOZORDER);
//移动标签控件下面的第一个按钮。
hwndButton = GetDlgItem(hwndDlg, BTN_CLOSE);
SetWindowPos(hwndButton,NULL,
rcTab.left,rcTab.bottom + cyMargin,0,0,
SWP_NOSIZE | SWP_NOZORDER);
//确定按钮的大小。
GetWindowRect(hwndButton, &rcButton);
rcButton.right -= rcButton.left;
rcButton.bottom -= rcButton.top;
//将第二个按钮移到第一个按钮的右侧。
hwndButton = GetDlgItem(hwndDlg, BTN_TEST);
SetWindowPos(hwndButton,NULL,
rcTab.left + rcButton.right + cxMargin,
rcTab.bottom + cyMargin,0,0,
SWP_NOSIZE | SWP_NOZORDER);
//设置对话框的大小。
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);
//模拟第一项的选择。
OnSelChanged(hwndDlg);
}
// DoLockDlgRes - 加载和锁定对话框模板资源。
//返回指向锁定资源的指针。
// lpszResName - 资源的名称
DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName)
{
HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG);
HGLOBAL hglb = LoadResource(g_hinst, hrsrc);
return (DLGTEMPLATE *) LockResource(hglb);
}
以下功能处理主对话框的TCN_SELCHANGE通知消息。该函数会破坏出站页面的对话框(如果有)。然后使用CreateDialogIndirect函数为传入页创建一个无模式对话框。
// OnSelChanged - 处理TCN_SELCHANGE通知。
// hwndDlg - 父对话框的句柄
VOID WINAPI OnSelChanged(HWND hwndDlg)
{
DLGHDR *pHdr = (DLGHDR *) GetWindowLong(
hwndDlg, GWL_USERDATA);
int iSel = TabCtrl_GetCurSel(pHdr->hwndTab);
//销毁当前的子对话框(如果有)。
if (pHdr->hwndDisplay != NULL)
DestroyWindow(pHdr->hwndDisplay);
//创建新的子对话框。
pHdr->hwndDisplay = CreateDialogIndirect(g_hinst,
pHdr->apRes[iSel], hwndDlg, ChildDialogProc);
}
以下功能处理每个子对话框的WM_INITDIALOG消息。您不能指定使用CreateDialogIndirect功能创建的对话框的位置。此函数使用SetWindowPos功能将子对话框定位在选项卡控件的显示区域中。
// OnChildDialogInit - 将子对话框置于下降位置
//在标签控件的显示区域内。
VOID WINAPI OnChildDialogInit(HWND hwndDlg)
{
HWND hwndParent = GetParent(hwndDlg);
DLGHDR *pHdr = (DLGHDR *) GetWindowLong(
hwndParent, GWL_USERDATA);
SetWindowPos(hwndDlg,HWND_TOP,
pHdr- > rcDisplay.left,pHdr- > rcDisplay.top,
0, 0, SWP_NOSIZE);
}