基本上我有一个可以携带其他控件的自定义窗口,比方说按钮,位图,文本框等等,现在问题是,如果项目出窗外,我的意思是如果我试图创建像20个按钮明显它会离开窗口,所以我想我应该创建一个滚动条,使窗口滚动。很明显你不能添加WS_VSCROLL或WS_HSCROLL,因为它的向上和向下箭头都不会被点击,但如果你试图拖动它,它会跳回到顶部。所以我想我最好用createwindow()函数创建一个滚动条并控制剩下的东西。
以下是我如何创建窗口本身和滚动条的代码: 代码:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, "Name", WS_OVERLAPPEDWINDOW/*WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU*/,
CW_USEDEFAULT, 0, 1014, 585, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
WNDCLASSEX wcs;
wcs.cbSize = sizeof(wcs);
wcs.lpszClassName = szClassName;
wcs.hInstance = GetModuleHandle(0);
wcs.lpfnWndProc = CustWndProc;
wcs.hCursor = LoadCursor(NULL, IDC_ARROW);
wcs.hIcon = 0;
wcs.lpszMenuName = 0;
wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcs.style = 0;
wcs.cbClsExtra = 0;
wcs.cbWndExtra = 0;
wcs.hIconSm = 0;
if(!RegisterClassEx(&wcs))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwndCtrl = CreateWindowEx(
0L, // give it a standard border
szClassName,
_T("A custom control"),
WS_VISIBLE|WS_CHILD|WS_BORDER,
0, 0, 0, 0,
hWnd,
NULL, GetModuleHandle(0), CustWndProc
);
ShowWindow (hwndCtrl, SW_SHOW);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
这是处理其消息的代码:
LRESULT CALLBACK CustWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
RECT rc = {};
GetClientRect(hwnd, &rc);
const SIZE sz = { rc.right - rc.left, rc.bottom - rc.top };
SCROLLINFO si;
switch(msg)
{
case WM_MOUSEHOVER:
::MessageBox(hwnd, "Enter", "Info", MB_OK);
return 0;
case WM_CREATE:
int w , h;
w = 10;
h = 10;
HWND buttons;
for(h=10;h<500; h+=35){
buttons = CreateWindow("BUTTON", "How", WS_VISIBLE|WS_CHILD, w, h, 50, 30, hwnd, (HMENU)1231,NULL, NULL);
}
int width, height;
width = LOWORD(lParam); // Width Size of hWnd
height = HIWORD(lParam);
Scrollbar = CreateWindowEx(0L,
"SCROLLBAR",
NULL, // There is no text to display
WS_CHILD | WS_VISIBLE | SBS_VERT,
980,
47,
18,
405,
hWnd,
NULL,
hInst,
NULL
);
return 0;
case WM_INITDIALOG:
ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = 1000;
si.nPage = 10;
si.nPos = 54;
SetScrollInfo(Scrollbar, SB_CTL, &si, TRUE);
return TRUE;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return FALSE;
}
正如你所看到的那样,有一些按钮是用for循环自动创建的,之后滚动条本身正在创建,现在我不知道如何让它向下和向上滚动等等。 欢迎所有回复
答案 0 :(得分:2)
一个快速而又肮脏的例子:
case WM_VSCROLL:
{
auto 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;
WCHAR buf[20];
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;
auto hdc = GetDC(hWnd);
LPtoDP(hdc, &pt, 1);
ReleaseDC(hWnd, hdc);
ScrollWindow(hWnd, 0, -pt.y, NULL, NULL);
g_scrollY = pos;
return 0;
}
case WM_CREATE:
{
for (int i = 0; i < 100; ++i) {
auto hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 10, 30 * i, 250, 21, hWnd, NULL, hInst, NULL);
wchar_t buf[10];
StringCchPrintf(buf, 10, L"%d", i);
SetWindowText(hEdit, buf);
}
RECT rc = { 0 };
GetClientRect(hWnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 30 * 99 + 21;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hWnd, SB_VERT, &si, true);
return 0;
}
效果:
完整代码:http://pastebin.com/byE1xFsb
由于OP无法理解代码,这里有解释:
窗口的整个高度应为30 * 99 + 21. 99是编辑计数,21是编辑控件的高度。
窗口的滚动范围应为[0, 30 * 99 + 21 - client-area-height]
。 30 * 99 + 21 - client-area-height + client-area-height
等于30 * 99 + 21
,这是窗口的整个高度。
要确保上述滚动范围,
si.nMax = 30 * 99 + 21;
si.nPage = (rc.bottom - rc.top);
答案 1 :(得分:1)
以下是 Jichao 的改编代码,用于与孩子一起滚动静态。
// hStaticWnd should have WS_CLIPCHILDREN flag
#define WMU_SET_SCROLL_HEIGHT WM_USER + 100 // custom message
...
SetWindowLong(hStaticWnd, GWL_WNDPROC, (LONG)&cbNewScroll);
...
SendMessage(hColumnsWnd, WMU_SET_SCROLL_HEIGHT, 500, 0);
...
SendMessage(hColumnsWnd, WMU_SET_SCROLL_HEIGHT, 0, 0); // to turn off the scroll
LRESULT CALLBACK cbNewScroll(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_VSCROLL: {
WORD action = LOWORD(wParam);
int scrollY = (int)GetProp(hWnd, TEXT("SCROLLY"));
int pos = action == SB_THUMBPOSITION ? HIWORD(wParam) :
action == SB_THUMBTRACK ? HIWORD(wParam) :
action == SB_LINEDOWN ? scrollY + 30 :
action == SB_LINEUP ? scrollY - 30 :
-1;
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 p{0, pos - scrollY};
HDC hdc = GetDC(hWnd);
LPtoDP(hdc, &p, 1);
ReleaseDC(hWnd, hdc);
ScrollWindow(hWnd, 0, -p.y, NULL, NULL);
SetProp(hWnd, TEXT("SCROLLY"), (HANDLE)pos);
return 0;
}
break;
case WM_ERASEBKGND: {
HDC hDC = (HDC)wParam;
RECT rc{0};
GetClientRect(hWnd, &rc);
HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
FillRect(hDC, &rc, hBrush);
DeleteObject(hBrush);
return 1;
}
break;
case WMU_SET_SCROLL_HEIGHT: {
RECT rc = { 0 };
GetWindowRect(hWnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = wParam;
si.nPage = rc.bottom - rc.top;
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hWnd, SB_VERT, &si, true);
}
break;
case WM_DESTROY: {
RemoveProp(hWnd, TEXT("SCROLLY"));
}
break;
}
return CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam);
}
答案 2 :(得分:0)
这里有一个example from microsot,它可以处理文本,但是可以很容易地将其适应固定高度的组件。而本文how-to-control-scrollbar-in-vc-win32-api恰好符合问题的要求,并且代码清晰易读。
答案 3 :(得分:0)
我知道现在已经晚了5年,但是如果有人来找,我认为至少对OP的第二个问题有一个答案是很高兴的(因为Jichao回答了原始问题)。
哦,男孩不敢相信这几乎行不通,仍然有一些问题 就像滚动有一个限制。我的意思是它可以显示约50 或某行即使我没有那么多项目,也没有, 我知道问题是si.nMax = 30 * 99 + 21;告诉它 限制为30 * 99 + 21,但是必须有一些东西 必须花很多钱才能使用窗口行,并且只显示那么多。
因此,窗口的早期截止实际上是由于“ GetClientRect(hWnd,&rc);”造成的。我在自己的项目中发现将其更改为“ GetWindowRect(hWnd,&rc);”。并在创建(或调整大小)窗口后设置RECT解决了该问题。
//Called after Window Creation (not in WM_Create! After CreateWindowEx called!) or during Resize.
void ResetScrollbarSize(HWND hWnd)
{
RECT rc = { 0 };
GetWindowRect(hWnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 30 * 99 + 21;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hWnd, SB_VERT, &si, true);
}
该功能可以解决问题。同样,调用并完成了在 CreateWindowEx之后使用它的重要部分,因此Window需要获取RECT。