⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 faq34.htm

📁 C++builder学习资料C++builder
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<HTML>

<HEAD>

   <TITLE>Eliminate flicker when painting on a form or a TPaintBox control.</TITLE>

   <META NAME="Author" CONTENT="Harold Howe">

</HEAD>

<BODY BGCOLOR="WHITE">



<CENTER>

<TABLE  BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH="640">



<TR>

<TD>



<H3>

Eliminate flicker when painting on a form or a TPaintBox control.

</H3>



<P>

A lot of programmers complain about flicker when they draw on a <TT>TPaintBox</TT>

control, when they draw on the form's <TT>Canvas</TT>, or when they paint on the

<TT>Canvas</TT> of a control that they have written. One newsgroup post

contained a subject line that read: "TPaintBox wicked flicker, can it be

stopped." The answer is yes, and the solution is simple, once you understand

what is going on.

<UL>

<LI><A HREF="#explanation">Explanation of flicker</A>

<LI><A HREF="#example"    >An example of flicker</A>

<LI><A HREF="#tcontrol"   >Flicker in TControl and TGraphicControl objects</A>

<LI><A HREF="#tpaintbox"  >The wrong way to use TPaintBox or TImage</A>

<LI><A HREF="#remove"     >Removing flicker</A>

<LI><A HREF="#notes"      >Notes</A>

</UL>

</P>

<H3>

<A NAME="explanation">Explanation of flicker:</A>

</H3>

<P>

Before you eliminate flicker, it's helpful to know what it is. Windows sends

your program <TT>WM_PAINT</TT> messages to notify you that some part of

the screen needs to be repainted. This happens when your program first starts,

when you restore or maximize the program, and when the program is uncovered from

beneath another program. You can also ask Windows to send a <TT>WM_PAINT</TT>

message by calling the <TT>InvalidateRect</TT> API call.

</P>

<P>

Any window that responds to <TT>WM_PAINT</TT> messages must call the <TT>BeginPaint</TT> and

<TT>EndPaint</TT> API functions. These API functions might sound foreign to you if you've

been programming with OWL, the VCL, or MFC because these frameworks call the

functions for you (see <TT>TWinControl::PaintHandler</TT> in <TT>CONTROLS.PAS</TT>).

Among other things, <TT>BeginPaint</TT> will send your window a <TT>WM_ERASEBKGND</TT> if the

window is marked for erasing. The window will almost always be marked for erasing if

Windows sent the <TT>WM_PAINT</TT> message on its own. If the <TT>WM_PAINT</TT> message

was sent because you called <TT>InvalidateRect</TT>, then the window will be marked for

erasing if the last argument to <TT>InvalidateRect</TT> was true.

</P>

<P>

The flicker that you see is caused by the default handler for the <TT>WM_ERASEBKGND</TT>

message. Whenever the <TT>DefWindowProc</TT> receives a <TT>WM_ERASEBKGND</TT> message, it

erases the contents of the window by filling the window with the background color of

the form. The default handler is is equivalent to executing this code:

</P>

<pre>

Canvas<b>-></b>Brush<b>-></b>Color <b>=</b> Color<b>;</b>

Canvas<b>-></b>FillRect<b>(</b>ClientRect<b>)</b><b>;</b>

</pre>

<P>

Realize that this handler executes when your window calls <TT>BeginPaint</TT> after

receiving a <TT>WM_PAINT</TT> message. Your entire window will be erased before

<TT>BeginPaint</TT> returns. Your window will remain erased until you paint over

it. Generally, you paint the window in your <TT>WM_PAINT</TT> handler sometime

after <TT>BeginPaint</TT> has been called. The flicker that you see is caused by

<TT>BeginPaint</TT> erasing the background of your window just before your

<TT>WM_PAINT</TT> handler paints the window. In fact, you can change the color

of the flicker by changing the <TT>Color</TT> property of the form.

</P>

<H3>

<A NAME="example">An example of flicker:</A>

</H3>

<P>

To demonstrate the flicker phenomena, it's beneficial to slow down the message

handling process so the flicker becomes more apparent. The following code

example does just that. Create a new project and and add a button to the main

form (put it near the bottom). Set the form's <TT>Color</TT> property to <TT>clBlue</TT>.

Then modify the header and source files as shown below (create the <TT>OnClick</TT>

and <TT>OnPaint</TT> handler's using the Object Inspector).

</P>

<pre>

<font color="navy">// Header file</font>

<font color="green">#ifndef MAINFORMH</font>

<font color="green">#define MAINFORMH</font>

<font color="navy">//---------------------------</font>

<font color="green">#include &lt;vcl\Classes.hpp></font>

<font color="green">#include &lt;vcl\Controls.hpp></font>

<font color="green">#include &lt;vcl\StdCtrls.hpp></font>

<font color="green">#include &lt;vcl\Forms.hpp></font>

<font color="navy">//---------------------------</font>



<b>class</b> TForm1 <b>:</b> <b>public</b> TForm

<b>{</b>

<b>__published</b><b>:</b>	<font color="navy">// IDE-managed Components</font>

   TButton <b>*</b>Button1<b>;</b>

   <b>void</b> <b>__fastcall</b> Button1Click<b>(</b>TObject <b>*</b>Sender<b>)</b><b>;</b>

   <b>void</b> <b>__fastcall</b> FormPaint<b>(</b>TObject <b>*</b>Sender<b>)</b><b>;</b>

<b>private</b><b>:</b>	<font color="navy">// User declarations</font>

   <b>void</b> <b>__fastcall</b> WMEraseBkgnd<b>(</b>TWMEraseBkgnd <b>&</b>Message<b>)</b><b>;</b>

   <b>void</b> <b>__fastcall</b> WMPaint<b>(</b>TWMPaint <b>&</b>Message<b>)</b><b>;</b>

<b>public</b><b>:</b>		<font color="navy">// User declarations</font>

   <b>__fastcall</b> TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b><b>;</b>

BEGIN_MESSAGE_MAP

   MESSAGE_HANDLER<b>(</b>WM_ERASEBKGND<b>,</b>TWMEraseBkgnd<b>,</b>WMEraseBkgnd<b>)</b>

   MESSAGE_HANDLER<b>(</b>WM_PAINT<b>,</b> TWMPaint<b>,</b> WMPaint<b>)</b><b>;</b>

END_MESSAGE_MAP<b>(</b>TForm<b>)</b>

<b>}</b><b>;</b>





<font color="navy">// CPP file</font>

<font color="navy">//-----------------------</font>

<font color="green">#include &lt;vcl\vcl.h></font>

<font color="green">#pragma hdrstop</font>

<font color="green">#include "MAINFORM.h"</font>

<font color="navy">//-----------------------</font>

<font color="green">#pragma resource "*.dfm"</font>

TForm1 <b>*</b>Form1<b>;</b>

<font color="navy">//-----------------------</font>

<b>__fastcall</b> TForm1<b>:</b><b>:</b>TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b>

   <b>:</b> TForm<b>(</b>Owner<b>)</b>

<b>{</b>

<b>}</b>

<font color="navy">//-----------------------------------------</font>

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>WMEraseBkgnd<b>(</b>TWMEraseBkgnd <b>&</b>Message<b>)</b>

<b>{</b>

    OutputDebugString <b>(</b><font color="blue">"Inside WM_ERASEBKGND handler"</font><b>)</b><b>;</b>

    TForm<b>:</b><b>:</b>Dispatch<b>(</b><b>&</b>Message<b>)</b><b>;</b>   <font color="navy">// pass message to default handler</font>

    Sleep<b>(</b><font color="blue">5000</font><b>)</b><b>;</b>                 <font color="navy">// delay so flicker becomes obvious</font>

    OutputDebugString <b>(</b><font color="blue">"Leaving WM_ERASEBKGND handler"</font><b>)</b><b>;</b>

<b>}</b>

<font color="navy">//-----------------------------------------</font>

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>WMPaint<b>(</b>TWMPaint <b>&</b>Message<b>)</b>

<b>{</b>

    OutputDebugString <b>(</b><font color="blue">"Just received WM_PAINT message. Before BeginPaint"</font><b>)</b><b>;</b>

    TForm<b>:</b><b>:</b>Dispatch<b>(</b><b>&</b>Message<b>)</b><b>;</b>

    OutputDebugString <b>(</b><font color="blue">"Finished handling WM_PAINT message."</font><b>)</b><b>;</b>

<b>}</b>

<font color="navy">//-----------------------------------------</font>

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>Button1Click<b>(</b>TObject <b>*</b>Sender<b>)</b>

<b>{</b>

    OutputDebugString <b>(</b><font color="blue">"About to call invalidate"</font><b>)</b><b>;</b>

    <b>:</b><b>:</b>InvalidateRect<b>(</b>Handle<b>,</b>NULL<b>,</b>TRUE<b>)</b><b>;</b>

    OutputDebugString <b>(</b><font color="blue">"Just returned from Invalidate call"</font><b>)</b><b>;</b>

<b>}</b>

<font color="navy">//-----------------------------------------</font>

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>FormPaint<b>(</b>TObject <b>*</b>Sender<b>)</b>

<b>{</b>

    OutputDebugString <b>(</b><font color="blue">"Just entered form's OnPaint. BeginPaint already called."</font><b>)</b><b>;</b>

    Canvas<b>-></b>Brush<b>-></b>Color <b>=</b> clBlack<b>;</b>

    Canvas<b>-></b>FillRect<b>(</b>ClientRect<b>)</b><b>;</b>

    OutputDebugString <b>(</b><font color="blue">"Leaving OnPaint handler."</font><b>)</b><b>;</b>

<b>}</b>

</pre>

<P>

Run the program. Push the button a few times and observe how the background

is erased before the <TT>OnPaint</TT> handler executes. Close the program and then

look at the OutDbg1.TXT file. Locate the point where the first <TT>InvalidateRect</TT>

call was made. You should see this sequence of events:

</P>

<PRE>

     About to call invalidate

     Just returned from Invalidate call

     Just received WM_PAINT message. Before BeginPaint

     Inside WM_ERASEBKGND handler

     Leaving WM_ERASEBKGND handler

     Just entered form's OnPaint. BeginPaint already called.

     Leaving OnPaint handler.

     Finished handling WM_PAINT message.

</PRE>

<H3>

<A NAME="tcontrol">Flicker in <TT>TControl</TT> and <TT>TGraphicControl</TT> objects:</A>

</H3>

<P>

The explanation of <TT>WM_ERASEBKGND</TT> only applies to descendents of <TT>TWinControl</TT>

because only <TT>TWinControl</TT> descendents have window handles. <TT>TGraphicControl</TT>

and <TT>TControl</TT> don't have window handles, and as such, they have no concept of

<TT>DefWindowProc</TT>, <TT>BeginPaint</TT>, and <TT>WM_ERASEBKGND</TT>. However,

descendent's of these two classes can still suffer from flicker (note that <TT>TPaintBox</TT>

is derived from <TT>TGraphicControl</TT>). The key lies within the <TT>InvalidateControl</TT>

method of TControl. This function calls <TT>InvalidateRect</TT> for the parent window:

</P>

<PRE>



procedure TControl.InvalidateControl(IsVisible, IsOpaque: Boolean);

begin

   ...

   ...

   InvalidateRect(Parent.Handle, @Rect, not (IsOpaque or

   (csOpaque in Parent.ControlStyle) or BackgroundClipped));



   ...

end

</PRE>



<P>

<TT>Parent-&gt;Handle</TT> is the <TT>HWND</TT> of the form or panel that the control

is placed on. <TT>Rect</TT> is equal to the <TT>BoundsRect</TT> of the control.

The <TT>InvalidateRect</TT> call is telling Windows to invalidate the region of

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -