2015-12-08 71 views
0

我想向tabcontrol中的标签页添加一些控件,但它似乎将被添加到所有页面,并且tabcontrol中没有默认标签页。将控件添加到TabControl中的特定标签页Win32

我已阅读下面的这些链接,但他们没有帮助我,在他们的某些部分,困惑了我。

How to add controls to a Tab control

http://www.cplusplus.com/forum/windows/37161/

https://msdn.microsoft.com/en-us/library/bb760551.aspx

https://msdn.microsoft.com/en-us/library/hh298366.aspx

https://msdn.microsoft.com/en-us/library/ms645398.aspx

这里是我的代码:

[编号]:

#define ID_LBL 500    
#define ID_BTN 501    
#define ID_TBC 502    

HWND hWnd; 

void InserTabItem(HWND handle, LPWSTR text, int id) 
{ 
TCITEM tci = { 0 }; 
tci.mask = TCIF_TEXT; 
tci.pszText = text; 
tci.cchTextMax = wcslen(text); 
SendMessage(handle, TCM_INSERTITEM, id, LPARAM(&tci)); 
} 

LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) 
{ 
switch (Msg) 
{ 
case WM_CREATE: 
{ 
    HWND button_handle = 0; 
    HWND label_handle = 0; 
    HWND tab_handle = 0; 
    tab_handle = CreateWindowEx(WS_EX_CONTROLPARENT, WC_TABCONTROL, 0, WS_VISIBLE | WS_CHILD, 10, 10, 200, 150, hWnd, HMENU(ID_TBC), 0, 0); 
    InserTabItem(tab_handle, L"page1", 0); 
    InserTabItem(tab_handle, L"page2", 1); 
    button_handle = CreateWindowEx(0, WC_BUTTON, L"test-button-page2", WS_VISIBLE | WS_CHILD, 10, 50, 150, 30, tab_handle, HMENU(ID_BTN), 0, 0); 
    label_handle = CreateWindowEx(0, WC_STATIC, L"test-label-page1", WS_VISIBLE | WS_CHILD, 10, 100, 150, 30, tab_handle, HMENU(ID_LBL), 0, 0); 
} 
break; 
case WM_CLOSE: 
    DestroyWindow(hWnd); 
    break; 
case WM_DESTROY: 
    PostQuitMessage(0); 
    break; 
default: 
    return DefWindowProc(hWnd, Msg, wParam, lParam); 
    break; 
} 

return 0; 
} 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreviewInstance, LPSTR lpcmdline, int ncmdshow) 
{ 
WNDCLASSEX wndexcls; 
wndexcls.lpszClassName = L"win"; 
wndexcls.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
wndexcls.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 
wndexcls.hCursor = LoadCursor(NULL, IDC_ARROW); 
wndexcls.hbrBackground = (HBRUSH)(COLOR_3DSHADOW + 1); 
wndexcls.lpszMenuName = NULL; 
wndexcls.style = NULL; 
wndexcls.hInstance = hInstance; 
wndexcls.cbSize = sizeof(WNDCLASSEX); 
wndexcls.cbClsExtra = 0; 
wndexcls.cbWndExtra = 0; 
wndexcls.lpfnWndProc = WndProc; 
RegisterClassEx(&wndexcls); 

hWnd = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT, L"win", L"TestApp", WS_OVERLAPPEDWINDOW, 100, 100, 640, 380, 0, 0, hInstance, 0); 
ShowWindow(hWnd, ncmdshow); 
UpdateWindow(hWnd); 



MSG wnd_msg; 
while (GetMessage(&wnd_msg, NULL, 0, 0)>0) 
{ 
    TranslateMessage(&wnd_msg); 
    DispatchMessage(&wnd_msg); 
} 
return (int)wnd_msg.wParam; 

}

我要寻找一个安全和有效执行。

感谢所有帮助

====================================== ==================

[更新]:

感谢评论,但在细节:(

没有答案虽然不是我正在寻找的实现(NotDialogBased),但从我提到的第四个链接:

如何创建一个标签的对话框:

https://msdn.microsoft.com/en-us/library/hh298366.aspx

这是我该网页的代码:

[RESOURCE.H]:

#define IDD_Page1      101 
#define IDD_Page2      102 
#define IDD_Page3      103 
#define IDD_Main_Dialog     104 
#define IDC_BTN_Page1     1001 
#define IDC_BTN2_Page1     1002 
#define IDC_BTN_Page2     1013 
#define IDC_BTN_Page3     1014 

[RESOURCE.RC]:

#include "resource.h" 

#define APSTUDIO_READONLY_SYMBOLS 
#include "winres.h" 
#undef APSTUDIO_READONLY_SYMBOLS 
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 

////////////////////////////////////////////////// 
// 
// Dialog 
    // 

IDD_Page1 DIALOGEX 0, 0, 313, 178 
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD 
FONT 8, "MS Shell Dlg", 400, 0, 0x1 
BEGIN 
PUSHBUTTON  "Button2-Page1",IDC_BTN2_Page1,129,107,67,22 
PUSHBUTTON  "Button-Page1",IDC_BTN_Page1,127,77,67,22 
END 

IDD_Page2 DIALOGEX 0, 0, 309, 177 
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD 
FONT 8, "MS Shell Dlg", 400, 0, 0x1 
BEGIN 
PUSHBUTTON  "Button-Page2",IDC_BTN_Page2,120,77,60,18 
END 

IDD_Page3 DIALOGEX 0, 0, 309, 177 
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD 
FONT 8, "MS Shell Dlg", 400, 0, 0x1 
BEGIN 
PUSHBUTTON  "Button-Page3",IDC_BTN_Page3,120,73,64,25 
END 

IDD_Main_Dialog DIALOGEX 0, 0, 309, 177 
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD 
FONT 8, "MS Shell Dlg", 400, 0, 0x1 
BEGIN 
END 


//////////////////////////////////////////// 
// 
// DESIGNINFO 
// 

#ifdef APSTUDIO_INVOKED 
GUIDELINES DESIGNINFO 
BEGIN 
IDD_Page1, DIALOG 
BEGIN 
    LEFTMARGIN, 7 
    RIGHTMARGIN, 306 
    TOPMARGIN, 7 
    BOTTOMMARGIN, 171 
END 

IDD_Page2, DIALOG 
BEGIN 
    LEFTMARGIN, 7 
    RIGHTMARGIN, 302 
    TOPMARGIN, 7 
    BOTTOMMARGIN, 170 
END 

IDD_Page3, DIALOG 
BEGIN 
    LEFTMARGIN, 7 
    RIGHTMARGIN, 302 
    TOPMARGIN, 7 
    BOTTOMMARGIN, 170 
END 

IDD_Main_Dialog, DIALOG 
BEGIN 
    LEFTMARGIN, 7 
    RIGHTMARGIN, 302 
    TOPMARGIN, 7 
    BOTTOMMARGIN, 170 
END 
END 
#endif // APSTUDIO_INVOKED 

#endif  

[Main.cpp]:

#include <windows.h> 
#include <CommCtrl.h> 
#include "resource.h" 
#pragma comment(lib, "ComCtl32.lib") 
#define C_PAGES 3 

INT_PTR CALLBACK DialogProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); 

HWND Win_Handle; 
HWND Dailog_Handle; 
HINSTANCE hInstance_Win_Global; 

typedef struct { 
WORD  dlgVer; 
WORD  signature; 
DWORD  helpID; 
DWORD  exStyle; 
DWORD  style; 
WORD  cDlgItems; 
short  x; 
short  y; 
short  cx; 
short  cy; 
WORD  pointsize; 
WORD  weight; 
BYTE  italic; 
BYTE  charset; 
} DLGTEMPLATEEX; 

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; 

void InserTabItem(HWND handle, LPWSTR text, int id) 
{ 
TCITEM tci = { 0 }; 
tci.mask = TCIF_TEXT; 
tci.pszText = text; 
tci.cchTextMax = wcslen(text); 
SendMessage(handle, TCM_INSERTITEM, id, LPARAM(&tci)); 
} 

DLGTEMPLATEEX* DoLockDlgRes(LPCTSTR lpszResName) 
{ 
HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG); 
HGLOBAL hglb = LoadResource(hInstance_Win_Global, hrsrc); 
return (DLGTEMPLATEEX *)LockResource(hglb); 
} 

VOID WINAPI OnChildDialogInit(HWND hwndDlg) 
{ 
HWND hwndParent = GetParent(hwndDlg); 
DLGHDR *pHdr = (DLGHDR *)GetWindowLong(
    hwndParent, GWL_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; 
} 
VOID 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(hInstance_Win_Global, 
    (DLGTEMPLATE *)pHdr->apRes[iSel], hwndDlg,DialogProc); 
} 
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; 

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

DLGHDR *pHdr = (DLGHDR *)LocalAlloc(LPTR, sizeof(DLGHDR)); 
SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)pHdr); 

pHdr->hwndTab = CreateWindow(
    WC_TABCONTROL, L"", 
    WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 
    0, 0, 300, 200, 
    hwndDlg, NULL, hInstance_Win_Global, NULL 
    ); 
if (pHdr->hwndTab == NULL) 
{ 
    return HRESULT_FROM_WIN32(GetLastError()); 
} 

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); 

pHdr->apRes[0] = DoLockDlgRes(MAKEINTRESOURCE(IDD_Page1)); 
pHdr->apRes[1] = DoLockDlgRes(MAKEINTRESOURCE(IDD_Page2)); 
pHdr->apRes[2] = DoLockDlgRes(MAKEINTRESOURCE(IDD_Page3)); 

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; 
} 

MapDialogRect(hwndDlg, &rcTab); 

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, IDC_BTN_Page1); 
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, IDC_BTN2_Page1); 
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); 

return S_OK; 
} 

LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) 
{ 
switch (Msg) 
{ 
case WM_CREATE: 
{ 
    Dailog_Handle = CreateDialogParam(hInstance_Win_Global, MAKEINTRESOURCE(IDD_Main_Dialog), hWnd, DialogProc, 0); 
    ShowWindow(Dailog_Handle, SW_SHOWDEFAULT); 
    UpdateWindow(Dailog_Handle); 
    SetWindowPos(Dailog_Handle, 0, 10, 10, 500, 300, SWP_NOZORDER); 

} 
break; 
case WM_CLOSE: 
    DestroyWindow(hWnd); 
    break; 
case WM_DESTROY: 
    PostQuitMessage(0); 
    break; 
default: 
    return DefWindowProc(hWnd, Msg, wParam, lParam); 
    break; 
} 

return 0; 
} 
INT_PTR CALLBACK DialogProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) 
{ 
switch (Msg) 
{ 
case WM_INITDIALOG: 
{ 

    OnTabbedDialogInit(hWnd); 
    OnChildDialogInit(hWnd); 

    return (INT_PTR)TRUE; 
} 
    break; 
case WM_NOTIFY: 
{ 
    switch (((LPNMHDR)lParam)->code) 
    { 
    case TCN_SELCHANGE: 
    { 
     OnSelChanged(hWnd); 
    } 
    break; 
    default: 
     break; 
    } 

} 
break; 
case WM_CLOSE: 
    DestroyWindow(hWnd); 
    break; 
case WM_DESTROY: 
    PostQuitMessage(0); 
    break; 
} 
return (INT_PTR)FALSE; 
} 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreviewInstance, LPSTR lpcmdline, int ncmdshow) 
    { 
    WNDCLASSEX wndexcls; 
    wndexcls.lpszClassName = L"win"; 
    wndexcls.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wndexcls.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 
    wndexcls.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wndexcls.hbrBackground = (HBRUSH)(COLOR_3DSHADOW + 1); 
    wndexcls.lpszMenuName = NULL; 
    wndexcls.style = NULL; 
    wndexcls.hInstance = hInstance; 
    wndexcls.cbSize = sizeof(WNDCLASSEX); 
    wndexcls.cbClsExtra = 0; 
    wndexcls.cbWndExtra = 0; 
    wndexcls.lpfnWndProc = WndProc; 
    RegisterClassEx(&wndexcls); 

Win_Handle = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT, L"win", L"TestApp", WS_OVERLAPPEDWINDOW, 100, 100, 640, 380, 0, 0, hInstance, 0); 
hInstance_Win_Global = hInstance; 
ShowWindow(Win_Handle, SW_SHOWDEFAULT); 
UpdateWindow(Win_Handle); 


MSG wnd_msg; 
while (GetMessage(&wnd_msg, NULL, 0, 0)>0) 
{ 
    TranslateMessage(&wnd_msg); 
    DispatchMessage(&wnd_msg); 
} 
return (int)wnd_msg.wParam; 
} 

问题是,调试后,应用程序出现并且不显示任何内容。如果我评论OnSelChanged(hWnd)和OnChildDialogInit(hWnd) ,应用程序将正常启动并显示tabcontrol,但不显示页面上的控件。似乎问题在这里。

输出日志:

First-chance exception at 0x00BE1886 in testcppapp.exe: 0xC0000005: Access violation reading location 0x00000014. 
The program '[16220] testcppapp.exe' has exited with code 0 (0x0). 

我已阅读以下有关访问冲突读取位置的链接:

http://www.cplusplus.com/forum/general/17094/

但我不能修复该问题。

请发表您的答案并解释一下,不只是在评论中简要说明!

感谢您的任何帮助。

+2

选项卡控件实际上没有“页面”,它只是显示选项卡。您有责任将控件分成页面并适当地显示或隐藏它们。通常,为每个页面使用子对话框比单独创建所有控件更容易。 –

+0

@JonathanPotter:任何好的学习资源或示例代码。我提到的第四个链接令人困惑。任何单独创建所有控件的例子?谢谢 – BlueFlower

+0

正确设置标签控件是一项工作。你知道如何创建对话框吗?如果是这样,那么创建一个对话框作为子窗口很容易;只需添加'DS_CONTROL'和'WS_CHILD'样式即可。你也知道如何使用'WM_NOTIFY'?您需要知道选项卡被点击的时间,因为您有责任自行显示和隐藏实际页面。 (以后我可能会写一个关于创建选项卡控件的答案,它应该是社区wiki的东西吗?) – andlabs

回答

2

的一个问题是在这里:

pHdr->hwndDisplay = CreateDialogIndirect(hInstance_Win_Global, 
    (DLGTEMPLATE*)pHdr->apRes[iSel], hwndDlg, DialogProc); 

您正在重用的两个主对话框和儿童对话框相同的对话框过程。主对话框创建子对话框,子对话框使用相同的过程来创建子对话框...并且没有错误检查。

除此之外,这段代码太复杂了。只需使用主窗口的对话框。创建一个新的对话框IDD_DIALOG1并在其中拖放选项卡控件。为标签控件ID分配IDC_TAB1。请尝试从此代码开始,而不是:

#include <Windows.h> 
#include <CommCtrl.h> 
#include "Resource.h" 

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

HINSTANCE g_hinst; 

struct TData { 
    HWND page1, page2, page3; 
    HWND tab; 
} data; 

BOOL CALLBACK DialogPage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) 
{ 
    switch(msg) { 
    case WM_COMMAND: 
     switch (wp) { 
      //... 
     } 
    } 
    return FALSE; 
} 

void OnSelChange() { 
    int sel = TabCtrl_GetCurSel(data.tab); 
    ShowWindow(data.page1, (sel == 0) ? SW_SHOW : SW_HIDE); 
    ShowWindow(data.page2, (sel == 1) ? SW_SHOW : SW_HIDE); 
} 

BOOL CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) 
{ 
    switch (msg) { 
    case WM_INITDIALOG: { 
     data.page1 = CreateDialog(g_hinst, MAKEINTRESOURCE(IDD_Page1), hwnd, DialogPage); 
     data.page2 = CreateDialog(g_hinst, MAKEINTRESOURCE(IDD_Page2), hwnd, DialogPage); 

     data.tab = GetDlgItem(hwnd, IDC_TAB1); 
     if (data.tab) 
     { 
      TCITEM tci = { 0 }; 
      tci.mask = TCIF_TEXT; 
      tci.pszText = L"Page1"; 
      TabCtrl_InsertItem(data.tab, 0, &tci); 
      tci.pszText = L"Page2"; 
      TabCtrl_InsertItem(data.tab, 1, &tci); 

      RECT rc;//find tab control's rectangle 
      GetWindowRect(data.tab, &rc); 
      POINT offset = { 0 }; 
      ScreenToClient(hwnd, &offset); 
      OffsetRect(&rc, offset.x, offset.y); //convert to client coordinates 
      rc.top += 50; 
      SetWindowPos(data.page1, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_HIDEWINDOW); 
      SetWindowPos(data.page2, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_HIDEWINDOW); 

      OnSelChange(); 
     } 

     break; 
    } 

    case WM_NOTIFY: { 
     switch (((LPNMHDR)lp)->code) { 
     case TCN_SELCHANGE: 
      OnSelChange(); 
      break; 
     } 
    } 
    break; 

    case WM_COMMAND: 
     switch (wp) { 
     case IDOK: EndDialog(hwnd, wp); break; 
     case IDCANCEL: EndDialog(hwnd, wp); break; 
     } 
    } 

    return FALSE; 
} 

int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE, LPWSTR, int) 
{ 
    g_hinst = hinst; 
    DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc); 
    return 0; 
} 
+0

感谢您的回复。 +1为您的帖子投票。 – BlueFlower

+0

完美,干净的代码,没有C++类包装垃圾...我喜欢它:)一个建议虽然..删除该结构TData,只是宣布内部为全局变量,这将使它看起来更好..当我看到结构I自以为自己会为每个选项卡创建一个这样的结构体......但事实并非如此,它只是一个结构体,因此完全不需要它。 – SSpoke

+1

另一件事家伙总是在一个单独的线程中创建'DialogBox()',否则所有的代码将无法工作,直到DialogBox终止为止'DestoryWindow(hInst);/EndDialog(...,...)' – SSpoke

相关问题