如何获得 window 区域的准确大小,包括隐藏控件?
How do I get the exact size of a window's area, including hidden controls?
我需要获取整个 window 的显示区域大小,包括隐藏的控件。为此,我从第一个 window 的顶部获取值,从最后一个控件获取底部的值。但是,我在显示区域的末尾得到一个 gap/white。看看下图中的样子。显示区域应该是直到看到编辑控件的边框。我错过了什么?
我通过以下方式得到这个尺码:
int scrollHeight(void)
{
int minTop = 0;
int maxBottom = 0;
RECT rt = {0};
if(allControls_indx == 0) {
return 0;
}
#if 0
assert(allControls[0] == hMainWindow);
assert(allControls[allControls_indx - 1] == hEdit1);
#endif
if(!GetWindowRect(allControls[0], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
minTop = rt.top;
if(!GetWindowRect(allControls[allControls_indx - 1], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
maxBottom = rt.bottom;
return maxBottom - minTop;
}
像这样设置滚动条的页面大小:
setUpScrollBar(hwnd, scrollHeight());
其中 setUpScrollBar
定义为:
void setUpScrollBar(HWND hwnd, int h)
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = h;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
完整代码:
#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);
RECT GetLocalCoordinates(HWND hWnd);
int scrollHeight(void);
int getHeight(HWND control);
void setUpScrollBar(HWND hwnd, int);
void pushControl(HWND);
int displayArea(void);
void setScrollBarSize(HWND hwnd, int s);
inline int my_max(int a, int b);
inline int my_min(int a, int b);
void displayText(HWND h);
HINSTANCE ghInstance;
HWND hTab;
HWND hLabel1, hLabel2;
HWND hEdit1;
HWND hRemoveButton;
enum
{
IDBUTTON_REMOVE = 50
};
#define MAX_CONTROLS 8
static const wchar_t *title[] = { L"Button A1", L"Button B2", L"Button C3",
L"Button D4", L"Button E5", L"Button F6",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004",
L"Button 005", L"Button 006" };
HWND hButton[sizeof(title)/sizeof(title[0])] = {0};
HWND allControls[MAX_CONTROLS];
HWND hMainWindow;
int allControls_indx = 0;
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/2; // half than the usual size, so that the scrollbar show up and we can test it
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"main 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)
{
static int g_scrollY;
switch(msg)
{
case WM_CREATE:
hMainWindow = hwnd;
pushControl(hwnd); // push main window too
hLabel1 = CreateWindowW(L"Static", L"This is label 1...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 10, 130, 25, hwnd, (HMENU) 18, NULL, NULL);
pushControl(hLabel1);
hLabel2 = CreateWindowW(L"Static", L"This is label 2...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 40, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
hRemoveButton = CreateWindow(L"Button", L"Remove",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
200, 40, 80, 25,
hwnd,
(HMENU) IDBUTTON_REMOVE,
NULL,
NULL);
pushControl(hLabel2);
CreateTab(hwnd);
CreateButtons(hwnd);
setUpScrollBar(hwnd, scrollHeight());
break;
case WM_VSCROLL:
{
int action = LOWORD(wParam);
//HWND hScroll = (HWND)lParam;
int pos = -1;
if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
pos = HIWORD(wParam);
} else if (action == SB_LINEDOWN) {
pos = g_scrollY + 30;
} else if (action == SB_LINEUP) {
pos = g_scrollY - 30;
}
if (pos == -1)
break;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = pos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
pos = si.nPos;
POINT pt;
pt.x = 0;
pt.y = pos - g_scrollY;
HDC hdc = GetDC(hwnd);
LPtoDP(hdc, &pt, 1);
ReleaseDC(hwnd, hdc);
ScrollWindow(hwnd, 0, -pt.y, NULL, NULL);
g_scrollY = pos;
return 0;
}
break;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDBUTTON_REMOVE:
ShowWindow(hEdit1, SW_HIDE);
SetScrollRange(hwnd, SB_VERT, 0, displayArea(), TRUE);
break;
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// this is like scrollHeight but control that are hidden
// aren't considered part of the display area.
int displayArea(void)
{
int size = scrollHeight();
// start i with 1 so we skip the mainwindow.
// we are interested in the "children controls"
for(int i = 1; i < MAX_CONTROLS && allControls[i]; i++)
{
HWND h = allControls[i];
// if it not visible, remove it from display area
if(!IsWindowVisible(h)) {
size -= getHeight(h);
}
}
return size;
}
void pushControl(HWND hwnd)
{
if(allControls_indx > MAX_CONTROLS) {
assert(!"no room for extra controls");
}
allControls[allControls_indx++] = hwnd;
}
void CreateTab(HWND hwnd)
{
hTab =
CreateWindow(WC_TABCONTROLW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP,
100, 80, 400, 250,
hwnd,
(HMENU) 1,
NULL,
NULL);
InsertTabItem(hTab, 2, L"Tab 1");
InsertTabItem(hTab, 3, L"Tab b");
pushControl(hTab);
}
void CreateButtons(HWND hwnd)
{
RECT rt = GetLocalCoordinates(hTab);
TabCtrl_AdjustRect(hTab, FALSE, &rt);
RECT rt2 = {0};
GetWindowRect(hTab, &rt2);
int tab_width = rt2.right - rt2.left;
int tab_height = rt2.bottom - rt2.top;
int id = 4;
const int cy_breakSize = 25;
int cx_initPos = rt.left;
int cy_initPos = rt.top;
int cx = cx_initPos;
int cy = cy_initPos;
const int button_width = 80;
const int button_height = 25;
const int cx_margin = 10;
int nMaxButtonPerRow = tab_width / (button_width + cx_margin);
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize;
cx = cx_initPos;
}
hButton[i] =
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
button_width,
button_height,
hwnd, (HMENU) id++, NULL, NULL);
cx += button_width;
}
const int edit_width = 180;
const int edit_height = 25;
hEdit1 = CreateWindow(L"Edit", L"Hello, world!",
WS_VISIBLE | WS_CHILD | WS_BORDER,
cx,
// put below tab control's display area
getHeight(hTab) + cx,
edit_width,
edit_height,
hwnd,
(HMENU) id++,
NULL, NULL);
pushControl(hEdit1);
cx += edit_width;
}
void setUpScrollBar(HWND hwnd, int h)
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = h;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
void setScrollBarSize(HWND hwnd, int s)
{
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_RANGE;
si.nMin = 0;
si.nMax = s;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
int getHeight(HWND control)
{
RECT rt;
if(!GetWindowRect(control, &rt)) {
ErrorExit(NAMEOF(getHeight), __LINE__, __FILENAME__);
}
return rt.bottom - rt.top;
}
void displayText(HWND h)
{
int len = GetWindowTextLength(h);
if(len == 0) {
return;
}
wchar_t buffer[len + 1];
memset(buffer, 0, sizeof(buffer));
GetWindowText(h, buffer, len+1);
MessageBox(NULL, buffer, L"control text = ", MB_OK);
}
int scrollHeight(void)
{
int minTop = 0;
int maxBottom = 0;
RECT rt = {0};
if(allControls_indx == 0) {
return 0;
}
#if 0
assert(allControls[0] == hMainWindow);
assert(allControls[allControls_indx - 1] == hEdit1);
#endif
if(!GetWindowRect(allControls[0], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
minTop = rt.top;
if(!GetWindowRect(allControls[allControls_indx - 1], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
maxBottom = rt.bottom;
return maxBottom - minTop;
}
RECT GetLocalCoordinates(HWND hWnd)
{
RECT Rect;
GetWindowRect(hWnd, &Rect);
MapWindowPoints(HWND_DESKTOP, GetParent(hWnd), (LPPOINT) &Rect, 2);
return Rect;
}
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_INSERTITEM, id, (LPARAM) &tci);
}
void InitComControls()
{
INITCOMMONCONTROLSEX icex;
/* initialize this component is required to use tab control,
it seems.
*/
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
}
// display the error message from last error seen then
// exit the application, with that last error code seen.
// to test function, do something like:
// if(!GetProcessId(NULL))
// errorExit(TEXT("GetProcessId"));
// not quite a unittest but yeah.
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
/*
* NOTE!!: calling GetLastError() must be done before calling
* any other function, that would reset the GetLastError(), making
* this function report error about the wrong function.
*/
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;
}
我觉得你的计算方法没有问题。但是你第一次添加了主windowhwnd
的句柄,导致你在GetWindowRect(allControls[0], &rt)
中得到的minTop
是主的顶坐标window,造成显示区域末端出现空隙
但是,您实际上应该从hLabel1
中获取minTop
的坐标,这样您就不会参加该问题了。
此外,您忘记将 hRemoveButton
添加到 allControls
数组中。
解决:可以选择不把主window加到allControls
数组中,或者用GetWindowRect(allControls[1], &rt)
获取minTop
.[=25的坐标=]
if (!GetWindowRect(allControls[1], &rt)) {
ErrorExit((LPWSTR)NAMEOF(scrollHeight), __LINE__, (LPWSTR)__FILENAME__);
}
minTop = rt.top;
编辑:
部分编辑框被裁剪的原因是你的hLabel1
设置了比顶部高10,所以当hLabel1
的顶部作为minTop
,底部长度减少10个单位
您需要添加最顶部控件的 y 大小:
return maxBottom - minTop + y; //The y size of the topmost control.
我需要获取整个 window 的显示区域大小,包括隐藏的控件。为此,我从第一个 window 的顶部获取值,从最后一个控件获取底部的值。但是,我在显示区域的末尾得到一个 gap/white。看看下图中的样子。显示区域应该是直到看到编辑控件的边框。我错过了什么?
我通过以下方式得到这个尺码:
int scrollHeight(void)
{
int minTop = 0;
int maxBottom = 0;
RECT rt = {0};
if(allControls_indx == 0) {
return 0;
}
#if 0
assert(allControls[0] == hMainWindow);
assert(allControls[allControls_indx - 1] == hEdit1);
#endif
if(!GetWindowRect(allControls[0], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
minTop = rt.top;
if(!GetWindowRect(allControls[allControls_indx - 1], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
maxBottom = rt.bottom;
return maxBottom - minTop;
}
像这样设置滚动条的页面大小:
setUpScrollBar(hwnd, scrollHeight());
其中 setUpScrollBar
定义为:
void setUpScrollBar(HWND hwnd, int h)
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = h;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
完整代码:
#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);
RECT GetLocalCoordinates(HWND hWnd);
int scrollHeight(void);
int getHeight(HWND control);
void setUpScrollBar(HWND hwnd, int);
void pushControl(HWND);
int displayArea(void);
void setScrollBarSize(HWND hwnd, int s);
inline int my_max(int a, int b);
inline int my_min(int a, int b);
void displayText(HWND h);
HINSTANCE ghInstance;
HWND hTab;
HWND hLabel1, hLabel2;
HWND hEdit1;
HWND hRemoveButton;
enum
{
IDBUTTON_REMOVE = 50
};
#define MAX_CONTROLS 8
static const wchar_t *title[] = { L"Button A1", L"Button B2", L"Button C3",
L"Button D4", L"Button E5", L"Button F6",
L"Button G" , L"Button 001", L"Button 002",
L"Button 003", L"Button 004",
L"Button 005", L"Button 006" };
HWND hButton[sizeof(title)/sizeof(title[0])] = {0};
HWND allControls[MAX_CONTROLS];
HWND hMainWindow;
int allControls_indx = 0;
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/2; // half than the usual size, so that the scrollbar show up and we can test it
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"main 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)
{
static int g_scrollY;
switch(msg)
{
case WM_CREATE:
hMainWindow = hwnd;
pushControl(hwnd); // push main window too
hLabel1 = CreateWindowW(L"Static", L"This is label 1...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 10, 130, 25, hwnd, (HMENU) 18, NULL, NULL);
pushControl(hLabel1);
hLabel2 = CreateWindowW(L"Static", L"This is label 2...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 40, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
hRemoveButton = CreateWindow(L"Button", L"Remove",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
200, 40, 80, 25,
hwnd,
(HMENU) IDBUTTON_REMOVE,
NULL,
NULL);
pushControl(hLabel2);
CreateTab(hwnd);
CreateButtons(hwnd);
setUpScrollBar(hwnd, scrollHeight());
break;
case WM_VSCROLL:
{
int action = LOWORD(wParam);
//HWND hScroll = (HWND)lParam;
int pos = -1;
if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
pos = HIWORD(wParam);
} else if (action == SB_LINEDOWN) {
pos = g_scrollY + 30;
} else if (action == SB_LINEUP) {
pos = g_scrollY - 30;
}
if (pos == -1)
break;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = pos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
pos = si.nPos;
POINT pt;
pt.x = 0;
pt.y = pos - g_scrollY;
HDC hdc = GetDC(hwnd);
LPtoDP(hdc, &pt, 1);
ReleaseDC(hwnd, hdc);
ScrollWindow(hwnd, 0, -pt.y, NULL, NULL);
g_scrollY = pos;
return 0;
}
break;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDBUTTON_REMOVE:
ShowWindow(hEdit1, SW_HIDE);
SetScrollRange(hwnd, SB_VERT, 0, displayArea(), TRUE);
break;
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// this is like scrollHeight but control that are hidden
// aren't considered part of the display area.
int displayArea(void)
{
int size = scrollHeight();
// start i with 1 so we skip the mainwindow.
// we are interested in the "children controls"
for(int i = 1; i < MAX_CONTROLS && allControls[i]; i++)
{
HWND h = allControls[i];
// if it not visible, remove it from display area
if(!IsWindowVisible(h)) {
size -= getHeight(h);
}
}
return size;
}
void pushControl(HWND hwnd)
{
if(allControls_indx > MAX_CONTROLS) {
assert(!"no room for extra controls");
}
allControls[allControls_indx++] = hwnd;
}
void CreateTab(HWND hwnd)
{
hTab =
CreateWindow(WC_TABCONTROLW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP,
100, 80, 400, 250,
hwnd,
(HMENU) 1,
NULL,
NULL);
InsertTabItem(hTab, 2, L"Tab 1");
InsertTabItem(hTab, 3, L"Tab b");
pushControl(hTab);
}
void CreateButtons(HWND hwnd)
{
RECT rt = GetLocalCoordinates(hTab);
TabCtrl_AdjustRect(hTab, FALSE, &rt);
RECT rt2 = {0};
GetWindowRect(hTab, &rt2);
int tab_width = rt2.right - rt2.left;
int tab_height = rt2.bottom - rt2.top;
int id = 4;
const int cy_breakSize = 25;
int cx_initPos = rt.left;
int cy_initPos = rt.top;
int cx = cx_initPos;
int cy = cy_initPos;
const int button_width = 80;
const int button_height = 25;
const int cx_margin = 10;
int nMaxButtonPerRow = tab_width / (button_width + cx_margin);
for(int i = 0; i < sizeof(title)/sizeof(title[0]); ++i)
{
if(i != 0 && (i % nMaxButtonPerRow) == 0) {
cy += cy_breakSize;
cx = cx_initPos;
}
hButton[i] =
CreateWindow(L"button", title[i],
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
cx,
cy,
button_width,
button_height,
hwnd, (HMENU) id++, NULL, NULL);
cx += button_width;
}
const int edit_width = 180;
const int edit_height = 25;
hEdit1 = CreateWindow(L"Edit", L"Hello, world!",
WS_VISIBLE | WS_CHILD | WS_BORDER,
cx,
// put below tab control's display area
getHeight(hTab) + cx,
edit_width,
edit_height,
hwnd,
(HMENU) id++,
NULL, NULL);
pushControl(hEdit1);
cx += edit_width;
}
void setUpScrollBar(HWND hwnd, int h)
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = h;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
void setScrollBarSize(HWND hwnd, int s)
{
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_RANGE;
si.nMin = 0;
si.nMax = s;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
int getHeight(HWND control)
{
RECT rt;
if(!GetWindowRect(control, &rt)) {
ErrorExit(NAMEOF(getHeight), __LINE__, __FILENAME__);
}
return rt.bottom - rt.top;
}
void displayText(HWND h)
{
int len = GetWindowTextLength(h);
if(len == 0) {
return;
}
wchar_t buffer[len + 1];
memset(buffer, 0, sizeof(buffer));
GetWindowText(h, buffer, len+1);
MessageBox(NULL, buffer, L"control text = ", MB_OK);
}
int scrollHeight(void)
{
int minTop = 0;
int maxBottom = 0;
RECT rt = {0};
if(allControls_indx == 0) {
return 0;
}
#if 0
assert(allControls[0] == hMainWindow);
assert(allControls[allControls_indx - 1] == hEdit1);
#endif
if(!GetWindowRect(allControls[0], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
minTop = rt.top;
if(!GetWindowRect(allControls[allControls_indx - 1], &rt)) {
ErrorExit(NAMEOF(scrollHeight), __LINE__, __FILENAME__);
}
maxBottom = rt.bottom;
return maxBottom - minTop;
}
RECT GetLocalCoordinates(HWND hWnd)
{
RECT Rect;
GetWindowRect(hWnd, &Rect);
MapWindowPoints(HWND_DESKTOP, GetParent(hWnd), (LPPOINT) &Rect, 2);
return Rect;
}
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_INSERTITEM, id, (LPARAM) &tci);
}
void InitComControls()
{
INITCOMMONCONTROLSEX icex;
/* initialize this component is required to use tab control,
it seems.
*/
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
}
// display the error message from last error seen then
// exit the application, with that last error code seen.
// to test function, do something like:
// if(!GetProcessId(NULL))
// errorExit(TEXT("GetProcessId"));
// not quite a unittest but yeah.
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
/*
* NOTE!!: calling GetLastError() must be done before calling
* any other function, that would reset the GetLastError(), making
* this function report error about the wrong function.
*/
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;
}
我觉得你的计算方法没有问题。但是你第一次添加了主windowhwnd
的句柄,导致你在GetWindowRect(allControls[0], &rt)
中得到的minTop
是主的顶坐标window,造成显示区域末端出现空隙
但是,您实际上应该从hLabel1
中获取minTop
的坐标,这样您就不会参加该问题了。
此外,您忘记将 hRemoveButton
添加到 allControls
数组中。
解决:可以选择不把主window加到allControls
数组中,或者用GetWindowRect(allControls[1], &rt)
获取minTop
.[=25的坐标=]
if (!GetWindowRect(allControls[1], &rt)) {
ErrorExit((LPWSTR)NAMEOF(scrollHeight), __LINE__, (LPWSTR)__FILENAME__);
}
minTop = rt.top;
编辑:
部分编辑框被裁剪的原因是你的hLabel1
设置了比顶部高10,所以当hLabel1
的顶部作为minTop
,底部长度减少10个单位
您需要添加最顶部控件的 y 大小:
return maxBottom - minTop + y; //The y size of the topmost control.