创建选项卡控件
#define UNICODE #define _UNICODE #include <windows.h> #include <objidl.h> #include <gdiplus.h> #include <tchar.h> using namespace Gdiplus; //控件库以及相关宏 #include <windowsx.h>//有HANDLE_MSG宏+标准控件功能函数宏 #include <commctrl.h>//高级控件函数宏 #include<stdio.h> #include<tchar.h> #include <string> #define main_edit 101 //依赖库 #pragma comment (lib,"user32.lib") #pragma comment(lib, "comctl32.lib") #pragma comment (lib,"gdi32.lib") #pragma comment (lib,"gdiplus.lib") #pragma comment (lib,"kernel32.lib") /*全局变量区*/ HINSTANCE hInst; HWND Page[100]; std::wstring strexepath1 = L"Page1"; std::wstring strexepath2 = L"Page2"; std::wstring strexepath3 = L"Page3"; //资源区#pragma region Page1 #pragma region Page LRESULT CALLBACK Page2Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { HDC hdc; PAINTSTRUCT ps; switch (uMsg) { case WM_PAINT: { hdc = BeginPaint(hWnd, &ps); //////////////代码位置///////////////// Graphics graphics(hdc); graphics.SetSmoothingMode(SmoothingModeAntiAlias);//反锯齿设置 Pen pen(Color(255, 255, 0, 255)); graphics.DrawLine(&pen, 100, 200, 200, 100); //graphics.DrawRectangle(&pen, 100, 100, 200, 200); //////////////////////////////////// EndPaint(hWnd, &ps); } return 0; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } } LRESULT CALLBACK Page1Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { HDC hdc; PAINTSTRUCT ps; switch (uMsg) { case WM_PAINT: { hdc = BeginPaint(hWnd, &ps); //////////////代码位置///////////////// Graphics graphics(hdc); graphics.SetSmoothingMode(SmoothingModeAntiAlias);//反锯齿设置 Pen blackPen(Color(255, 0, 0, 0), 3); RectF rect1(10.0f, 10.0f, 100.0f, 50.0f); RectF rect2(40.0f, 40.0f, 100.0f, 50.0f); RectF rect3(80.0f, 4.0f, 50.0f, 100.0f); RectF rects[] = { rect1, rect2, rect3 }; RectF* pRects = rects; graphics.DrawRectangles(&blackPen, pRects, 3); //////////////////////////////////// EndPaint(hWnd, &ps); } return 0; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } } //功能:向Tab中添加标签 int InsertTabItem(HWND hTab, LPTSTR pszText, int iid) { TCITEM ti = { 0 }; ti.mask = TCIF_TEXT; ti.pszText = pszText; ti.cchTextMax = wcslen(pszText); return (int)SendMessage(hTab, TCM_INSERTITEM, iid, (LPARAM)&ti); } //Page自己的窗口类,通过SetWindowLongPtr()可以子类化 ATOM RegisterPageClass() { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInst; wcex.hIcon = NULL; wcex.hIconSm = NULL; wcex.lpszMenuName = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wcex.lpszClassName = TEXT("Page"); wcex.lpfnWndProc = DefWindowProc; return RegisterClassEx(&wcex); } HWND CreatePageWnd(HWND hTab, SUBCLASSPROC PageWndProc) { RECT rcTabDisplay; //获取Tab控件客户区的的大小(Tab窗口坐标系) GetClientRect(hTab, &rcTabDisplay);//获取Tab窗口的大小(而不是在屏幕坐标系中) TabCtrl_AdjustRect(hTab, FALSE, &rcTabDisplay);//通过窗口大小,获取客户区大小。 //创建与Tab控件客户区大小的画布 HWND hPage = CreateWindowEx( NULL, //window extend Style TEXT("Page"), // window class name NULL, // window caption WS_CHILD, // window style rcTabDisplay.left, // initial x position rcTabDisplay.top, // initial y position rcTabDisplay.right, // initial x size rcTabDisplay.bottom, // initial y size hTab, // parent window handle NULL, // window menu handle hInst, // program instance handle NULL); // creation parameters int err = GetLastError(); // SetWindowLongPtr(hPage, GWLP_WNDPROC, (LONG_PTR)PageWndProc);//子类化窗口 SetWindowSubclass(hPage, PageWndProc, main_edit, 1); return hPage; } /* 功能:向Tab中添加页面 实现原理:一个窗口多个窗口处理函数,每个窗口处理函数负责一个页面的布局和处理 注意:该添加方式是顺序添加,即AddAfter,在当前页面后添加 */ BOOL addPage(HWND hTab, LPTSTR strPageLabel, SUBCLASSPROC PageWndProc) { static int iPageIndex = 0; InsertTabItem(hTab, strPageLabel, iPageIndex); HWND hPage = CreatePageWnd(hTab, PageWndProc); Page[iPageIndex++] = hPage; return TRUE; } /*功能:初始化所有页面*/ BOOL InitPages(HWND hTab) { RegisterPageClass(); addPage(hTab, &strexepath1[0], Page1Proc); addPage(hTab, &strexepath2[0], Page2Proc); addPage(hTab, &strexepath3[0], Page2Proc); return TRUE; } #pragma endregion #pragma region TabControl HWND InitTabControl(HWND hParentWnd, HWND hWndFocus, LPARAM lParam) { INITCOMMONCONTROLSEX iccx; iccx.dwSize = sizeof(INITCOMMONCONTROLSEX); iccx.dwICC = ICC_TAB_CLASSES; if (!InitCommonControlsEx(&iccx)) return FALSE; // 创建TabControl控件,其实就是一个子窗口和该窗口上的标签,这些标签是Tab控件具有的,但没有与具体的页面相连,只是Tab控件的特有属性而已。 //////////////////////////////创建Tab控件///////////////////////////////////////////////////// RECT rc; GetClientRect(hParentWnd, &rc); HWND hTab = CreateWindowEx(0, WC_TABCONTROL, 0, TCS_FIXEDWIDTH | WS_CHILD | WS_VISIBLE, rc.left + 2, rc.top + 2, rc.right - 4, rc.bottom - 4, hParentWnd, NULL, hInst, 0); // Set the font of the tabs to a more typical system GUI font SendMessage(hTab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0); //根据页面来初始化页面 InitPages(hTab); ShowWindow(Page[0], SW_SHOW);//显示第一个页面 return hTab; } //三种窗口状态:SIZE_MINIMIZE,SIZE_MAXIMIZE,SIZE_RESTORED LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hTab; switch (message) { case WM_CREATE: hTab = InitTabControl(hWnd, NULL, lParam);//主窗口创建的时候,同时创建Tab子窗口 break; case WM_SIZE://改变完成之后 HIWORD(lParam); //客户区Height LOWORD(lParam);//Width RECT rcTab; // 缩放Tab //HIWORD(lParam)-----hWnd height,LOWORD(lParam)-----hWnd width MoveWindow(hTab, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); //缩放Page GetClientRect(hTab, &rcTab); TabCtrl_AdjustRect(hTab, FALSE, &rcTab); for (int i = 0; i < TabCtrl_GetItemCount(hTab); i++) MoveWindow(Page[i], rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, TRUE); break; case WM_NOTIFY://WM_NOTIFY消息中,lParam是一个NMHDR结构,code字段是具体通知消息,hwndFrom是发出通知的窗口Handle switch (((LPNMHDR)lParam)->code) { case TCN_SELCHANGING://Tab改变前 { int iPage = TabCtrl_GetCurSel(hTab); ShowWindow(Page[iPage], SW_HIDE); return FALSE; } case TCN_SELCHANGE://Tab改变后 { int iPage = TabCtrl_GetCurSel(hTab); ShowWindow(Page[iPage], SW_SHOW); return TRUE; } } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow) { static HWND hWnd; MSG msg; WNDCLASS wndClass; GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; hInst = hInstance; // Initialize GDI+. GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = MainWndProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = TEXT("GettingStarted"); RegisterClass(&wndClass); hWnd = CreateWindow( TEXT("GettingStarted"), // window class name TEXT("Getting Started"), // window caption WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } GdiplusShutdown(gdiplusToken); return msg.wParam; }
主要步骤:
- 创建主窗口, HWND hWnd = CreateWindow();
- 与此同时创建Tab子窗口,因为是放在WM_CREATE中,所以创建的过程等同于创建主窗口
(1)使用InitCommonControlsEx初始化Tab控件
(2)使用HWND hTab = CreateWindowEx(0, WC_TABCONTROL,,,,,)创建tab子窗口
3. Tab窗口创建完之后,需要创建tab窗口下的页面窗口(选项卡各个窗口都是独立)
(1)需要注册页面窗口类,这步是为了后面创建页面提供条件,RegisterPageClass(),不然创建页面窗口会显示未注册
(2)然后往Tab子窗口中添加标签,即tab1,tab2,tab3选项卡 ,使用 SendMessage(hTab, TCM_INSERTITEM, iid, (LPARAM)&ti);
(3)创建与Tab控件客户区大小的画布,即选项卡下的页面窗口,可以展示自定义内容,HWND hPage = CreateWindowEx(NULL, TEXT("Page"), , , ,)
4. 子类化页面窗口,这样可以为每个页面窗口指定窗口回调函数, 使用SetWindowSubclass
注意: WM_NOTIFY是在发生事件或控件需要某些信息时,由公共控件发送到其父窗口,在这个项目中是用作切换tab选项卡
WM_SIZE 是用作确定tab窗口和页面窗口的位置
TabCtrl_GetItemCount(hTab) 返回选项卡数,这里创建了3个选项卡,所以返回3
TabCtrl_AdjustRect 给定窗口矩形,计算选项卡控件的显示区域,或计算与指定显示区域相对应的窗口矩形