WinAPI C++主窗口与滚动子窗口Static控件重复及滚动交互问题
问题描述
我想要创建一个指定大小和位置的可滚动子窗口,窗口内用Static控件承载消息。尝试过不同类名后遇到以下问题:
- 使用
static类时,滚动条可见但无法交互; - 认为
edit类不合适,因为需要自定义绘制消息; - 已知子窗口(
messages_window)和主窗口使用相同类名导致逻辑混乱,但不知道如何修正,也不确定是否需要更改滚动条的创建方式。
代码如下:
#define UNICODE #define _UNICODE #include "windows.h" #include <stdlib.h> #include <stdio.h> #include <cwctype> #include <cwchar> #include <iostream> #include <cmath> LRESULT CALLBACK window_message_procedure(HWND, UINT, WPARAM, LPARAM); // fonst and style HFONT hFont; HFONT hErrorFont; HBRUSH hStaticBrush; int vscroll_pos = 0; HWND messages_window; HWND message_window; static HBRUSH hBrush = CreateSolidBrush(RGB(230,230,230)); // controls int WINAPI WinMain(HINSTANCE hInst, HINSTANCE HprevInst, LPSTR args, int ncmdshow) { // set window values const wchar_t* window_class_name = L"WindowClass"; const wchar_t* window_name = L"Window"; const int WINDOW_W = 500; const int WINDOW_H = 500; // create window WNDCLASSW wc = {0}; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // wc.hbrBackground = CreateSolidBrush(RGB(24, 24, 24)); // set cursor wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hInstance = hInst; wc.lpszClassName = window_class_name; // set procedure for window messages wc.lpfnWndProc = window_message_procedure; // register window class if (!RegisterClassW(&wc)) { return -1; } // create window HWND hWnd = CreateWindowW( // WS_EX_APPWINDOW, window_class_name, window_name, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_W, WINDOW_H, NULL, NULL, hInst, NULL ); messages_window = CreateWindowEx( 0, window_class_name, // it only works if I setting same class as main window L"", WS_CHILD | WS_VISIBLE | WS_VSCROLL, 0, 200, 500, 200, hWnd, NULL, hInst, NULL ); ShowWindow(messages_window, ncmdshow); UpdateWindow(messages_window); MSG message = {0}; // main loop while (GetMessage(&message, NULL, 0, 0)) { TranslateMessage(&message); DispatchMessage(&message); } return 0; } LRESULT CALLBACK window_message_procedure(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam) { switch (message) { // on window create case WM_CREATE: { HDC hdc = GetDC(hWnd); // set common font int fontSize = -MulDiv(14, GetDeviceCaps(hdc, LOGPIXELSY), 72); ReleaseDC(hWnd, hdc); hFont = CreateFontW( fontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, // smooth text VARIABLE_PITCH, L"Segoe UI" ); // error font fontSize = -MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72); ReleaseDC(hWnd, hdc); hErrorFont = CreateFontW( fontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, // smooth text VARIABLE_PITCH, L"Segoe UI" ); message_window = CreateWindowW( L"static", L"test text hadshfasdf", WS_VISIBLE | WS_CHILD | ES_MULTILINE, 0, 0, 60, 30, hWnd, NULL, NULL, NULL ); SCROLLINFO scroll_info; scroll_info.cbSize = sizeof(scroll_info); scroll_info.nPos = 0; scroll_info.nMax = 800; scroll_info.nPage = 400; scroll_info.fMask = SIF_ALL; SetScrollInfo(messages_window, SB_VERT, &scroll_info, TRUE); // EnableScrollBar(messages_window, SB_VERT, ESB_ENABLE_BOTH); break; } case WM_VSCROLL: { int oldPos = vscroll_pos; switch (LOWORD(wparam)) { case SB_LINEUP: vscroll_pos -= 10; break; case SB_LINEDOWN: vscroll_pos += 10; break; case SB_PAGEUP: vscroll_pos -= 50; break; case SB_PAGEDOWN: vscroll_pos += 50; break; case SB_THUMBTRACK: vscroll_pos = HIWORD(wparam); break; } vscroll_pos = std::max(0, std::min(400, vscroll_pos)); if (vscroll_pos != oldPos) { int delta = oldPos - vscroll_pos; ScrollWindow(messages_window, 0, delta, NULL, NULL); SetScrollPos(messages_window, SB_VERT, vscroll_pos, TRUE); UpdateWindow(messages_window); } return 0; } // close window case WM_DESTROY: PostQuitMessage(0); break; // menu or button click case WM_COMMAND: switch (wparam) { } } return DefWindowProcW(hWnd, message, wparam, lparam); }
编译命令:g++ test_scroll.cpp -mwindows -lcomctl32 -lgdi32
解决方案
1. 为滚动子窗口注册独立的窗口类
主窗口和子窗口共用同一个类会导致消息处理逻辑混乱,需要给滚动子窗口单独注册专属类:
// 在WinMain中注册主窗口类后,添加以下代码 const wchar_t* scroll_window_class = L"ScrollWindowClass"; WNDCLASSW scroll_wc = {0}; scroll_wc.hbrBackground = (HBRUSH)COLOR_WINDOW; scroll_wc.hCursor = LoadCursor(NULL, IDC_ARROW); scroll_wc.hInstance = hInst; scroll_wc.lpszClassName = scroll_window_class; scroll_wc.lpfnWndProc = window_message_procedure; // 可复用同一个消息过程 if (!RegisterClassW(&scroll_wc)) { return -1; } // 创建messages_window时使用新类名 messages_window = CreateWindowEx( 0, scroll_window_class, // 替换为新注册的滚动窗口类名 L"", WS_CHILD | WS_VISIBLE | WS_VSCROLL, 0, 200, 500, 200, hWnd, NULL, hInst, NULL );
2. 修正滚动消息的处理逻辑
当前WM_VSCROLL处理未区分消息来源,会导致主窗口和子窗口的滚动逻辑冲突,需添加窗口判断:
case WM_VSCROLL: { // 仅处理滚动子窗口的消息 if (hWnd != messages_window) { return DefWindowProcW(hWnd, message, wparam, lparam); } int oldPos = vscroll_pos; SCROLLINFO scroll_info = {0}; scroll_info.cbSize = sizeof(scroll_info); scroll_info.fMask = SIF_ALL; GetScrollInfo(hWnd, SB_VERT, &scroll_info); switch (LOWORD(wparam)) { case SB_LINEUP: scroll_info.nPos -= 10; break; case SB_LINEDOWN: scroll_info.nPos += 10; break; case SB_PAGEUP: scroll_info.nPos -= scroll_info.nPage; break; case SB_PAGEDOWN: scroll_info.nPos += scroll_info.nPage; break; case SB_THUMBTRACK: scroll_info.nPos = HIWORD(wparam); break; default: return DefWindowProcW(hWnd, message, wparam, lparam); } // 限制滚动范围,避免超出内容边界 scroll_info.nPos = std::max(0, std::min(scroll_info.nMax - (int)scroll_info.nPage, scroll_info.nPos)); if (scroll_info.nPos != oldPos) { int delta = oldPos - scroll_info.nPos; ScrollWindow(hWnd, 0, delta, NULL, NULL); // 滚动子窗口内的所有控件 scroll_info.fMask = SIF_POS; SetScrollInfo(hWnd, SB_VERT, &scroll_info, TRUE); // 更新滚动条位置 vscroll_pos = scroll_info.nPos; UpdateWindow(hWnd); } return 0; }
3. 将Static控件挂载到滚动子窗口
当前message_window创建在主窗口下,需修改父窗口为滚动子窗口,才能随滚动条移动:
message_window = CreateWindowW( L"static", L"test text hadshfasdf", WS_VISIBLE | WS_CHILD | SS_LEFT | SS_WORDELLIPSIS, // 替换为Static控件专属样式 0, 0, 60, 30, messages_window, // 父窗口改为滚动子窗口 NULL, NULL, NULL );
4. 修复Static控件的样式错误
ES_MULTILINE是Edit控件的样式,Static控件应使用SS_LEFT、SS_WORDELLIPSIS等专属样式,避免样式冲突。
内容的提问来源于stack exchange,提问作者madjetmax
相关产品推荐
相关产品推荐

