双缓冲常见控件

5

有没有一种方法可以对常用控件进行双缓冲处理?当前,当它们调整大小时会出现很多闪烁……

编辑:如果有帮助的话,这是一堆按钮控件和一些编辑控件,都位于选项卡控件之上。选项卡控件重新绘制自己,然后按钮控件重新绘制自己。当按钮重新绘制时,它们会闪烁。

编辑2:这是我遇到的问题示例: http://billy-oneal.com/Lobfuscator.exe


这里缺少很多信息。Windows的哪个版本?常用控件的哪个版本?你是如何调整大小的? - bmargulies
3
  1. 无论我使用哪个版本都无所谓。
  2. 6。
  3. DeferWindowPos()。
- Billy ONeal
8个回答

4
我知道这个话题很老,但对于遇到闪烁问题的人可能仍然相关。与Billy非常相似,当切换标签时,放置在标签上的控件显示和隐藏时会出现闪烁问题。为了参考,我广泛使用ShowWindow函数来隐藏和显示控件。
我已经调整WS_EX_COMPOSITED几个小时了,但结果非常奇怪。我也没有调整大小,对话框被设计成全屏运行,并根据当前桌面分辨率进行调整。
以下是我手动创建的对话框布局,为每个控件调用CreateWindowEx函数:
主窗口 -- 一些控件 -- 标签控件 ---- 更多控件
缩进表示父子关系。标签控件在创建时设置了WS_CHILD和WS_CLIPCHILDREN样式,所有控件都设置了WS_CHILD样式。
最后成功的方法如下。
MainProc proc hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    mov eax,uMsg
    cmp eax,WM_INITDIALOG
    je @WM_INITDIALOG
    ...

    invoke DefWindowProc,hWnd,uMsg,wParam,lParam
    ret

@WM_INITDIALOG:
    ...

    invoke GetWindowLong,hWnd,GWL_EXSTYLE
    or eax,WS_EX_COMPOSITED
    invoke SetWindowLong,hWnd,GWL_EXSTYLE,eax
    ...

MainProc endp

这段代码使用汇编语言(MASM32)编写,但我相信你可以理解。简单来说,在WM_INITDIALOG期间获取主窗口的EX_STYLE,并添加WS_EX_COMPOSITED。

在这种情况下,此方法适用于32位Windows XP SP3和64位Windows 7 SP1。不需要向任何选项卡的子控件添加WS_EX_COMPOSITED样式(我使用的某些静态控件已经设置了WS_EX_TRANSPARENT,但那是为其他原因),目前看来也没有必要在WM_ERASEBKGND消息上返回非零值。我在一台性能适中的C2D计算机上也没有遇到任何性能问题。

供参考,以下是我的Main

Main proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX,msg:MSG

    mov wc.cbSize,sizeof WNDCLASSEX
    mov wc.style,CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc,offset MainProc
    mov wc.cbClsExtra,NULL
    mov wc.cbWndExtra,DLGWINDOWEXTRA
    push hInst
    pop wc.hInstance
    mov wc.hbrBackground,COLOR_BTNFACE+1
    mov wc.lpszClassName,offset szClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov wc.hIcon,eax
    mov wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov wc.hCursor,eax
    invoke RegisterClassEx,addr wc
    invoke CreateDialogParam,hInstance,IDD_MAIN,NULL,addr MainProc,NULL
    invoke ShowWindow,hWin,SW_SHOWNORMAL
    invoke UpdateWindow,hWin
    invoke LoadAccelerators,hInstance,IDD_ACC_TABLE
    mov hAcc,eax
    jmp @2
@1:
    invoke TranslateAccelerator,hWin,hAcc,addr msg
    test eax,eax
    jne @2
    invoke TranslateMessage,addr msg
    invoke DispatchMessage,addr msg
@2:
    invoke GetMessage,addr msg,NULL,0,0
    test eax,eax
    jne @1

    mov eax,msg.wParam
    ret

Main endp

这里没有什么特别的。我将“对话框控制灰色”设置为背景颜色,并使用CS_*REDRAW样式,但这些似乎不影响此情况。 我用来创建主窗口的“空白”对话框模板如下:

IDD_MAIN DIALOGEX 0,0,318,177
FONT 8,"MS Sans Serif",0,0,0
CLASS "DLGCLASS"
STYLE 0x90800000
EXSTYLE 0x00000008
BEGIN
END

希望这份内容可以帮助那些寻找答案的人节省一些时间。虽然有点长,但我想尽可能详细地说明问题。
敬礼。

4

考虑使用 WS_EX_COMPOSITEDWS_EX_TRANSPARENT 样式。它们提供了双缓冲,尽管当底层位图完成绘制时会调用 WM_PAINT,因为它从下到上绘制子控件,所以你只能在窗口过程中绘制。我之前用过它,效果还不错。

将顶层窗口(容器)设置为扩展样式 WS_EX_COMPOSITED,将子窗口设置为 WS_EX_TRANSPARENT。此外,请记住定义:

#define WINVER 0x501 

请参考CreateWindowEx了解混合样式信息。这也使得子窗口可以进行每像素透明度。

更新

使用WM_PRINTCLIENT将客户端区域传输到DC上的位图,并作为一个整体复制整个客户端区域怎么样?

http://blogs.msdn.com/larryosterman/archive/2008/08/27/larry-s-new-favorite-windows-message-wm-printclient.aspx


我会调查一下。可能只需要打个勾就行了... 我只需要确保它能正常工作。除非有人提出在Win2k上可行的解决方案... - Billy ONeal
1
抱歉。 - Hernán
1
不要忘记 Larry 的后续帖子,Herman,在那里他解释说 WM_PRINTCLIENT 是一个丑陋的 hack,只是掩盖了问题(同时引起了其他问题),而不是真正解决闪烁问题。http://blogs.msdn.com/larryosterman/archive/2009/09/16/building-a-flicker-free-volume-control.aspx - Ian Boyd
我不知道有 WS_CLIPSIBLINGS 风格 - 我只是使用了 WS_CLIPCHILDREN,因为子控件不是选项卡控件的子控件,而是我的窗口的子控件,所以它是无效的。这似乎已经解决了选项卡控件的问题。(所以如果没有人回答我的问题,你将获得此赏金)然而,任何静态控件在调整大小时仍会闪烁 - 我怀疑这是静态控件的问题,如果可能的话,我仍然希望能够双缓冲它们... - Billy ONeal
我通过将WS_EX_COMPOSITED添加到容器中,解决了我的丑陋的多行编辑控件闪烁问题(它占据了窗口的90%)。显然不需要WS_EX_TRANSPARENT,它会引起一些奇怪的背景问题。 - quantum
显示剩余2条评论

2
Larry Osterman最近在博客中谈到了这个主题;您可能会在那里找到一些有趣的细节。

1
如果可以避免的话,我宁愿不必完全重新实现win32选项卡控件。 - Billy ONeal
但是关键观察是,底层控件(在您的情况下为选项卡控件)会导致其子控件重新绘制。对单个控件进行双缓冲处理是无效的。您需要确保它们不会失效,或者需要一种方法来缓冲所有绘图到窗口。Osterman最初的方法是使用WM_PRINTCLIENT hack进行缓冲,但这会导致其他问题。他的最终解决方案并没有涉及重新实现整个控件,只是绘画部分,这主要是几个函数调用。 - Adrian McCarthy
选项卡控件比群组框复杂得多。为了使其正常工作,它不仅仅是“绘制”的问题。 - Billy ONeal

0

确实,

有人在我之前的一篇帖子中回答了这个问题。请查看:这里

一定要给他点赞,因为他很棒。

代码是C#,希望有一个简单的翻译。


2
.NET 中的控件与 Win32 中的控件无关。 - Billy ONeal
你在提问时没有说明它是win32的。Pascal Couq是在我发布后添加的标签。 - Jrud
3
如果你注意到这个链接,它会跳转到Win32公共控件页面。我还注意到.NET不称它们为“common controls”。 - Billy ONeal
除了Jrud的评论之外,链接页面上提供的解决方案实际上设置了WS_EX_COMPOSITED……这正是其他所有人都提供的解决方案。由于操作系统是Windows,且这不是一个普通控件问题,而是一个关于Windows如何重绘的问题,因此他的建议是有效的。 - Jason D

0

如果不知道你在做什么,我猜测你可能正在使用MFC或Win32 C。

你可能想在WM_SIZE消息上进行调整大小。我不确定你在哪里调整控件的大小,但我认为你是在它被调整大小时进行调整,这就是为什么会导致闪烁的原因。

此外,我在思考但不确定,你可能可以使用SetWindowPos函数,并对于uFlags,使用SWP_NOREDRAW。虽然我不确定这对于常规控件的效果如何。


选项卡控件(我的窗口本质上是一个大的选项卡控件)需要重新绘制,然后调整大小。这就是我需要双缓冲区的原因。 - Billy ONeal

0

你可以在最开始创建一个内存设备上下文MemDC。将所有内容绘制到MemDC中,然后当窗口接收到WM_PAINT消息或被无效化时,通过位块传输将MemDC复制到真实的DC中。

我记得几年前在Herbert Schildt的书中(《Windows 98 Programming from the Ground Up》)读到了这种技术。这样做的好处是,由于将内存dc传输到真实dc中,因此所有的重绘都更快。但是有一个很大的问题,就是你想要使用多大的内存dc!但他展示了如何解决这个问题。该书由Osborne McGraw Hill出版,其中包含所有章节的代码下载。

希望这能帮到你, 最好的祝愿, 汤姆。


是的,但这对我没有帮助,因为我不是在进行绘制。通用控件应该负责这个。子控件不会发送WM_PAINT消息给我。 - Billy ONeal
"Big"在这些年里已经发生了变化。一个1920x1200的全屏幕只有7MB。 - Mark Ransom
2
不知道你从哪里得到那个数学结果,Ransom先生。1920x1200x32是737280000位,或者87.89MB。 - Billy ONeal
2
我计算了192012003,我的答案是兆字节而不是兆比特。 - Mark Ransom
3
假设颜色深度为32位,每个像素需要4个字节,那么1920*1200个像素需要大约9MB的存储空间。 - Sune Rievers

0

你没有使用WS_EX_TRANSPARENT,是吗?这会导致底层窗口在控件之前被绘制,当底层窗口擦除时会出现闪烁。


我甚至不知道那是什么。 - Billy ONeal

0
我们使用 WTL::CDoubleBufferImpl 混入来实现这个目的。我们甚至使用 GDI+ 进行绘图,完全没有闪烁。
使用方法非常简单:只需公开继承自 WTL::CDoubleBufferImpl<YourClass>,并将其链接到 ATL 消息映射中即可。

WTL命名空间的来源是什么? - Billy ONeal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接