Reputation: 16724
How do I get the tab control area coordinates then add buttons in this area? I don't know which one should I use TabCtrl_GetItemRect(), GetWindowRect() or something else, because it seems I'm not getting the coordinates I'd like, which is the tab control area, where I could put the buttons. It currently is positioned far from the tab control white-area, where I'd like to place them, as you can see in the image below. What's the proper way to put them within this tab control?
the function that deal with this is like this:
void CreateButtons(HWND hwnd)
{
RECT rt = {0};
//TabCtrl_GetItemRect(hTab, 0, &rt);
GetWindowRect(hTab, &rt);
//GetClientRect(hTab, &rt);
/*
wchar_t buffer[256] = {0};
wsprintf(buffer,
L"top = %d, bottom = %d, left = %d, right = %d",
rt.top, rt.bottom, rt.left, rt.right);
MessageBox(NULL, buffer, L"", MB_OK);
*/
int id = 4;
static const wchar_t *title[] = { L"Button A", L"Button B", L"Button C",
L"Button D", L"Button E", L"Button F",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004" };
const int nMaxButtonPerRow = 3;
const int cx_margin = 10;
const int cy_bottomMarge = 10;
const int cy_breakSize = 25;
int cx_initPos = (int)rt.left;
int cx = cx_initPos;
int cy = 50;
const int width = 80;
const int height = 25;
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize + cy_bottomMarge;
cx = cx_initPos;
}
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
width,
height,
hwnd, (HMENU) id++, NULL, NULL);
cx += width + cx_margin;
}
}
full code:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>
#ifdef UNICODE
#define STRSPLIT wcsrchr
#else
#define STRSPLIT strrchr
#endif
#define __FILENAME__ (STRSPLIT(TEXT(__FILE__), '/') ? STRSPLIT(TEXT(__FILE__), '/') + 1 : TEXT(__FILE__))
#define NAMEOF(s) TEXT(#s)
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK CreateTabProc(HWND, UINT, WPARAM, LPARAM);
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename);
void InitComControls();
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename);
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename);
void InitComControls();
void CreateTab(HWND hwnd);
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text);
void CreateButtons(HWND hwnd);
HINSTANCE ghInstance;
HWND hTab;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow)
{
MSG msg = {0};
HWND hwnd;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
InitComControls();
if(!RegisterClass(&wc)) {
ErrorExit(NAMEOF(RegisterClass), __LINE__, __FILENAME__);
}
int width = 500;
int height = 350;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int cx = (screenWidth - width) / 2;
int cy = (screenHeight - height) / 2;
hwnd = CreateWindowW(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
cx, cy, width, height, NULL, NULL,
hInstance, NULL);
ghInstance = hInstance;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindowW(L"Static", L"This is label 1...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 10, 130, 25, hwnd, (HMENU) 18, NULL, NULL);
CreateWindowW(L"Static", L"This is label 2...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 40, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
CreateTab(hwnd);
CreateButtons(hwnd);
//ChangeToDefaultFont(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void CreateTab(HWND hwnd)
{
hTab =
CreateWindow(WC_TABCONTROLW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP | WS_EX_CONTROLPARENT,
50, 80, 400, 250,
hwnd,
(HMENU) 1,
NULL,
NULL);
InsertTabItem(hTab, 2, L"Tab 1");
InsertTabItem(hTab, 3, L"Tab b");
}
void CreateButtons(HWND hwnd)
{
RECT rt = {0};
//TabCtrl_GetItemRect(hTab, 0, &rt);
GetWindowRect(hTab, &rt);
//GetClientRect(hTab, &rt);
/*
wchar_t buffer[256] = {0};
wsprintf(buffer,
L"top = %d, bottom = %d, left = %d, right = %d",
rt.top, rt.bottom, rt.left, rt.right);
MessageBox(NULL, buffer, L"", MB_OK);
*/
int id = 4;
static const wchar_t *title[] = { L"Button A", L"Button B", L"Button C",
L"Button D", L"Button E", L"Button F",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004" };
const int nMaxButtonPerRow = 3;
const int cx_margin = 10;
const int cy_bottomMarge = 10;
const int cy_breakSize = 25;
int cx_initPos = (int)rt.left;
int cx = cx_initPos;
int cy = 50;
const int width = 80;
const int height = 25;
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize + cy_bottomMarge;
cx = cx_initPos;
}
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
width,
height,
hwnd, (HMENU) id++, NULL, NULL);
cx += width + cx_margin;
}
}
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text)
{
TCITEMW tci = {0};
tci.mask = TCIF_TEXT;
tci.pszText = text;
tci.cchTextMax = lstrlenW(text);
SendMessage(tabHwnd, TCM_INSERTITEMW, id, (LPARAM) &tci);
}
void InitComControls()
{
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
}
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename)
{
DWORD dw = ShowLastError(lpszFunction, line, filename);
ExitProcess(dw);
}
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename)
{
#define MAX_DIGITS 16
DWORD dw = GetLastError();
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
lpDisplayBuf = (LPVOID) LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) +
lstrlen((LPCTSTR)lpszFunction) + 40 +
(line > 0 ? MAX_DIGITS : 0) +
(filename != NULL ? lstrlen(filename) : 0)) *
sizeof(TCHAR)
);
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with %d: %s"),
lpszFunction, dw, lpMsgBuf
);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
return dw;
}
UPDATE 1: managed to do that with GetWindowRect()
and MapWindowPoints()
. I was missing the former one. see this post. It gets the correct tab control coordinates but not yet the display area (the white area) where the controls are supposed to be on. How can I get the coordinates to this white area? if I manage to get the window region size where the tabs buttons (the ones inserted by TCM_INSERTITEM
message) are located, would also work (not sure if quite elegant) would just skip them when setting the cx_initPos
value.
up-to-date code:
void CreateButtons(HWND hwnd)
{
RECT rt = GetLocalCoordinates(hTab);
int id = 4;
static const wchar_t *title[] = { L"Button A", L"Button B", L"Button C",
L"Button D", L"Button E", L"Button F",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004" };
const int nMaxButtonPerRow = 3;
const int cx_margin = 10;
const int cy_bottomMarge = 10;
const int cy_breakSize = 25;
int cx_initPos = rt.left;
int cx = cx_initPos;
int cy = rt.top;
const int width = 80;
const int height = 25;
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize + cy_bottomMarge;
cx = cx_initPos;
}
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
width,
height,
hwnd, (HMENU) id++, NULL, NULL);
cx += width + cx_margin;
}
}
RECT GetLocalCoordinates(HWND hWnd)
{
RECT Rect;
GetWindowRect(hWnd, &Rect);
MapWindowPoints(HWND_DESKTOP, GetParent(hWnd), (LPPOINT) &Rect, 2);
return Rect;
}
what it looks like now:
Upvotes: 1
Views: 497
Reputation: 3890
If you need to add a button to the tab control, you should pass hTab
as the parent class of the button:
CreateButtons(hTab);
And according to the documentation:
x
Type: int
The initial horizontal position of the window. For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, in screen coordinates. For a child window, x is the x-coordinate of the upper-left corner of the window relative to the upper-left corner of the parent window's client area. If this parameter is set to CW_USEDEFAULT, the system selects the default position for the window's upper-left corner and ignores the y parameter. CW_USEDEFAULT is valid only for overlapped windows; if it is specified for a pop-up or child window, the x and y parameters are set to zero.
So the set coordinates are relative to the upper left corner of the parent window. You don't need to get the position information of hTab
through GetWindowRect
, but should pass the position of the relative tab control like the following code:
int cx_initPos = 10;
int cx = cx_initPos;
It works for me:
Supplement: If you need to adjust for different tab pages, you can refer to this thread.
Edit:
You can try adding TabCtrl_AdjustRect(hTab, FALSE, &rt);
after RECT rt = GetLocalCoordinates(hTab);
:
RECT rt;
rt = GetLocalCoordinates(hTab);
TabCtrl_AdjustRect(hTab, FALSE, &rt);
Is this the result you want:
Upvotes: 2