Link to home
Start Free TrialLog in
Avatar of cunningham
cunningham

asked on

How to stop screen flicker using MFC

I have an application that displays real-time data in a fairly large edit box. When new data is displayed, the screen always flickers which is very annoying. I am currently putting the data to be displayed in a CString object and then calling UpdateData() which tranfers the contents of the CString object to the edit box.

The only reference I can find to this problem states that the flicker is caused by the fact that windows always erases the entire background before it displays the new data. Is there a way to stop this flicker problem using MFC? Code examples would be helpful here.

Thanks for any information that you can give.
Avatar of Shay050799
Shay050799

by calling UpdateData(FALSE) u telling MFC to redraw it self, this calls the WM_PAINT (for CDialog derived classes)
and WM_DRAW (for CWnd derived classes)

what u can do is one of the following:
use the CString but do not do UpdateData do this:

CEdit* pEdit=(CEdit)GetDlgItem(IDC_MY_EDIT);
pEdit->SetWindowText(szNewText);  //szNewText is your CString

or try to comment the WM_PAIT/WM_DRAW code, but it won't help much cause u won't see the text.
try the first one it'll help

shay
pEdit->UpdateWindows();  
Avatar of cunningham

ASKER

Using the SetWindowText method you descibe does not work any better. In fact, if you follow the UpdateData() routine down to its lower levels, you will see that it is just calling SetWindowText also.

I do not understand what you mean by "try to comment the WM_PAIT/WM_DRAW code". Are you telling me to change that code (ie. OnPaint and OnDraw)? If so, what should I change it to?
try to comment it, don't chenge it put // at the beginning of the lines
You might want to try overriding the CEdit control with your own window, say CMyEdit.

Then override the CMyEdit::OnEraseBkgrd to only return false.  Then your edit control won't erase itself.  

Unfortunately, you'll have to worry about multiple characters overwriting themselves.  But at least, you'll be in control.

Phillip
If you're using InvalidateRect(), add "FALSE" as the second parameter and that will stop the flickering from occurring.
What about the following code? (Associate the edit box with CEdit)

const int nLen = m_edit.GetWindowTextLength();
m_edit.SetSel(nLen, nLen);
m_edit.ReplaceSel(str);
Look at this article . It may help you a bit ...

Flicker-Free Displays Using an Off-Screen DC

http://msdn.microsoft.com/library/techart/msdn_flicker.htm

Good Luck!
Overriding CEdit to a CMyEdit and then overriding OnEraseBkgrd as suggested seems to lessen the flicker athough it is still there. Maybe you can't get rid of it totally??? I found it strange that the characters did not overwrite themselves since I did not override any other function in my CMyEdit class. Can anyone explain why this is since I am not erasing the background???

The SetSel and ReplaceSel method did not work although I am not very familiar with these routines so it could have been operator error trying to fit them into my existing code.

I have seen the document "Flicker-Free Displays Using an Off-Screen DC" but, again, I am a newbie who does not know how to relate this stuff into an MFC program. I have not worked with Device Contexts much at all.

Thanks for all of the advice. I am at the point now where I don't know if there is such a thing as 'flicker-free' or if I should live with the nominal flicker I get with the overridden OnEraseBkgrd routine. Any more suggestions??

Dan
Try doing this: (these apply to the CEdit etc)

  SetRedraw(kFalse);
  UpdateDate();  
  SetRedraw(kTrue);
  Invalidate;

Cheers,

Raymond.

ASKER CERTIFIED SOLUTION
Avatar of naveenkohli
naveenkohli

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for all the help. The solution was found in the article "Flicker-Free Displays Using an Off-Screen DC" located in the MSDN library. The following is how to do it using MFC. Basically, you have to override the OnEraseBkgnd() and OnPaint() routines of the edit control that you are trying to eliminate the flicker from.

BOOL CMyEdit::OnEraseBkgnd(CDC *pDC)
{
   return TRUE;
}

void CMyEdit::OnPaint()
{
   CPaintDC dc( this ); // device context for painting
   CRect    rc;
   CDC      memDC;
   CBitmap  memBM;
   CBrush   hbrBkGnd;

   // Get the size of the edit box rectangle
   //
   GetClientRect( &rc );

   // Create a compatible DC in memory
   //
   memDC.CreateCompatibleDC( &dc );

   // Create a compatible bitmap big enough for the edit box rectangle
   //
   memBM.CreateCompatibleBitmap( &dc, rc.right - rc.left, rc.bottom - rc.top );

   // Select the bitmap into the memory DC
   //
   CBitmap *pBM = memDC.SelectObject( &memBM );

   // Erase the background by filling in the memory
   // DC with the appropriate background color
   //
   hbrBkGnd.CreateSolidBrush( GetSysColor(COLOR_WINDOW) );
   memDC.FillRect( &rc, &hbrBkGnd );

   // Select the font that was created in the constructor
   //
   memDC.SelectObject( &m_DataFont );

   // Draw the text image into the memory DC
   //
   memDC.SetBkMode( TRANSPARENT );
   memDC.SetTextColor( GetSysColor(COLOR_WINDOWTEXT) );
   memDC.DrawText( m_strData, &rc, DT_LEFT );

   // Blit the changes from the memory DC to the screen DC
   //
   dc.BitBlt( rc.left, rc.top,
              rc.right - rc.left,
              rc.bottom - rc.top,
              &memDC,
              0, 0,
              SRCCOPY );

   // Cleanup
   //
   memDC.SelectObject( pBM );
   memBM.DeleteObject();
}
I don't see any need for you to use an edit control since you draw the text directly.
When drawing text directly, the default processing erases the entire background and then fills in the text. The following is a snippet from the article that helped solve the problem:

"What makes this window flicker when we update it frequently? The answer is that Windows asks the window procedure to repaint the window as a two-step process. First, it sends a WM_ERASEBKGND message and then a WM_PAINT message. The default handling for the WM_ERASEBKGND message is to fill the area with the current window background color. So the sequence of events is first to fill the area with solid color and then to draw the text on top. The net result of doing this frequently is that the window state alternates between its erased state and its drawn state—it flickers."

The fix in the code above eliminates flicker by combining both of these operations in the OnPaint() routine.
Yeah, I mean you don't have to use an edit box to draw the text. Why don't you just draw the text to the client area of your window?