windows - 如何在按住滚动条的同时强制重绘 WIN32 滚动窗口
问题描述
我们注意到我们的 Windows 应用程序上的 ScrollWindowEx 存在问题。因为子窗口很多,如果按住滚动条四处走动,会导致DesktopWindowManager出现巨大的卡顿。
这记录在这里
所以,我已经取消了对 ScrollWindowEx 的调用,现在我可以毫无问题地移动我的滚动条,但是当我放开滚动条时,窗口及其所有子项只会在新位置绘制。
按住滚动条时,我的整个程序似乎停止并卡在消息循环中。如果我们可以强制滚动条附加到的窗口更新,这不是问题。但我该怎么做呢?
我试过这个
UpdateWindow(pPane);
RedrawWindow(pPane,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
它确实会更新和重绘 - 你可以看到故障 - 但它会在上次离开的位置重绘它。不是滚动条的新位置将设置它的位置。
我打电话
SetScrollInfo(...)
参数正确,否则无法正常工作
如何在按住滚动条的同时使用正确的参数重绘窗口及其子项?
这是我的滚动消息处理程序
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hWnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_LINEUP:
if(vsi.nPos>vsi.nMin)
vsi.nPos=vsi.nPos-10;
break;
case SB_PAGEUP:
vsi.nPos = vsi.nPos - vsi.nPage;
break;
case SB_LINEDOWN:
if(vsi.nPos<vsi.nMax)
vsi.nPos=vsi.nPos+10;
break;
case SB_PAGEDOWN:
vsi.nPos = vsi.nPos + vsi.nPage;
break;
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=pOW->dialogysize;
vsi.nPage=sy;
SetScrollInfo(hWnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hWnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
RedrawWindow(hWnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
}
当您移动滚动条时,窗口会重新绘制,但在您将鼠标按住滚动条时的相同位置。它仅在您松开鼠标按钮时更新位置。
谢谢
肖恩
编辑 - 我创建了一个重现行为的示例。请注意,我们有一个单独的窗口类来保存多个编辑窗口。我没有在这个演示中处理 WM_SIZE 和 WM_MOVE 消息,但代码应该可以在窗口中正常工作。
如果在中定义了 USESCROLL 并且您上下移动滚动条,则会导致 DesktopWindowManager 中的锁定
如果未注释 USESCROLL,则结构中的 trackPos 仍会更新并且我调用 reDrawWindow,但是当我滚动滚动条时子编辑窗口不会移动。
由于我不想被锁定,请问我怎样才能让他们移动?
再次感谢
肖恩
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Class";
RegisterClass(&wc);
wc.lpfnWndProc = PaneProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Pane";
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Class", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW| WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS | WS_THICKFRAME | WS_SYSMENU, // Window style
200,200,400,400,
NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
return 0;
}
HWND hwndPane = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Pane", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_VSCROLL, // Window style
220,220,400,400,
hwnd,NULL,hInstance,NULL);
if (hwndPane == NULL)
{
return 0;
}
for(int i=0;i<200;i++)
{
HWND eb = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), NULL,ES_AUTOHSCROLL | WS_CHILD| WS_VISIBLE, 16, 16+24*i, 64, 24, hwndPane, (HMENU)i+10000, hInstance, NULL);
char tmp[64];
sprintf(tmp,"%d",i);
SetWindowText(eb,tmp);
}
ShowWindow(hwnd, nCmdShow);
ShowWindow(hwndPane, nCmdShow);
// Run the message loop.
while(1)
{
MSG msg = { };
while (PeekMessage(&msg, NULL, 0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
float ScrollAmtY=-(vsi.nPos - iVScrollPos);
#define USESCROLL
#ifdef USESCROLL
int ok=ScrollWindowEx(hwnd ,0,ScrollAmtY,NULL,NULL,NULL,NULL,SW_INVALIDATE|SW_ERASE|SW_SCROLLCHILDREN);
#else
UpdateWindow(hwnd);
RedrawWindow(hwnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
#endif
}
return 0;
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
解决方案
推荐阅读
- c# - 如何在 Xamarin Forms 中存储列表?
- symfony - Twig :默认过滤器和布尔值
- c# - 如何在 .net api 文档中显示自定义请求示例?
- python - 在 Python 中具有更大程度控制的 HTTPServer
- php - laravel 上的格式化时间
- sympy - SymPy:名称“符号”未定义
- rest - RabbitMq RPC 与其他同步方法
- birt - 数据类型为十进制时如何在表格中将空值显示为 N/A
- read-the-docs - readthedocs.org 中的“私人”权限设置 - 它是如何工作的?
- c# - 如何在不复制的情况下剪切原始数组