Link to home
Start Free TrialLog in
Avatar of mannycalaveras
mannycalaveras

asked on

Flicker drawing with GDI+

I wrote a scrolling text marquee that renders text into a memory buffer (a System.Drawing.Bitmap) and uses a timer to update the scrolling text by rendering the buffer with Graphics.DrawImage.

I have tried everything and no matter what I always get flickers.

I enabled double buffering:

this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();


The Graphics object is created from a panel, which is used as canvas.

_oGraphics = oDest.CreateGraphics(); // oDest is a System.Windows.Forms.Panel


I created a memory buffer for the background and

// render the buffer
_oGraphics.DrawImage(_oImageBackground, 0, 0);

// Render canvas
if(_oCanvas != null)
     _oGraphics.DrawImage(_oCanvas.Canvas, _iCursor, 0);


Clearing the Graphics object is worse. I'm at loss...


Refresh is based on a timer at a 30 millisecond interval and _iCursor moves 2 pixels at a time. Display is otherwise smooth.


Thanks,
Rich.
Avatar of smegghead
smegghead
Flag of United Kingdom of Great Britain and Northern Ireland image

Where is your code for refreshing ?

Have you tried putting the code for 'drawimage' in the Paint event of the control, then calling invalidate() for that control, that will cause the paint event to be fired, which contains the relevant graphics object. It might be that the panel is refreshing itself anyway.

Smg.
Avatar of mannycalaveras
mannycalaveras

ASKER

// Code for refreshing, called by
gr.DrawImage(_oImageBackground, 0, 0);

if(_oCanvas != null)
      gr.DrawImage(_oCanvas.Canvas, _iCursor, 0);


I tried calling invalidate on the Panel in the timer Tick and using the control's Graphics object but it didn't work either.

Next try... refresh thread...
Refresh thread obviously did not make a difference...

Here's my refresh method

public void Render(/*System.Drawing.Graphics gr*/)
{
      do
      {
            try
            {
                  if(_bRunning && _oGraphics != null && _oCanvas != null)
                  {
                              _oGraphics.DrawImage(_oImageBackground, 0, 0); // oImageBackground is a System.Drawing.Bitmap
                        _oGraphics.DrawImage(_oCanvas.Canvas, _iCursor, 0); // Canvas is a System.Drawing.Bitmap

                        if((_iCursor = _iCursor-2) < -Convert.ToInt32(_oCanvas.Width))
                              _iCursor = Convert.ToInt32(_width);
                  }

                  Thread.Sleep(1000 / _iInterval);
            }
            catch(Exception/* ex*/)
            {
                  //clsUtils.NotifyException(ex);
                  Thread.CurrentThread.Abort();
            }
      }
      while(true);
}
Are you able to email me your code? my address is in my profile.
i think somewere in your code the background gets erased

so i would say it is worth trying this:

add an override to wndproc and handle the erase background there
protected override void WndProc(ref Message messg)
{
        // We do not want to erase the background,
        if ((int)WM.WM_ERASEBKGND == messg.Msg)
            messg.Msg = (int) WM.WM_NULL;
 
        base.WndProc(ref messg);
}


hth,
A.
Avatar of Bob Learned
You could add the ControlStyles.UserPaint, and use the Paint event (persistent), instead of using CreateGraphics (non-persistent).

Bob
Tried blocking the WM_ERASEBKGND for the panel control or the main window and it did not help.

I already tried using the Graphics object from the OnPaint message without any difference.

Problem seems to be that double buffering or any other display setting doesn't seem to be working.

This is supposed to turn on double buffering as well as styles that minimize flickering...

this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint |
      ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
this.UpdateStyles();


But it doesn't do anything. Whether I put this or not doesn't change anything.
Have you overridden the OnPaintBackground

            protected override void OnPaintBackground(PaintEventArgs e)
            {
                  // This section should be left blank - so that it doesn't draw the background..
                  //base.OnPaintBackground(e);
            }

Take a look at this link:
http://www.codeproject.com/cs/media/sprites.asp

This link actually refers to moving sprites/animated graphics, but it may hold the key to a flickerless scrolling textbox for you.

In a nutshell, it uses rectangles and only refreshes a small region.  At any rate, take look... at worst it will cost you 20 minutes of your life.  I found it rather interesting and easy to follow.
Yes. I have found the problem.

Rendering on the panel caused the problem.

The idea was to have the panel as a delimiter for the canvas.

Double buffering is enabled on the main application and controls do not support double buffering, only the main app.

So instead I use the panel as a bounding box, invisible, and display on the main application's using the panel's bounds as limits.

All examples I found used the main form window as render area so the case was never handled. There might be a way to enable double buffering on individual controls, however the SetStyle requires to be called from a Control control, which only the application window is.

Don't understand why it's not possible for any control as they windows anyway.
ASKER CERTIFIED SOLUTION
Avatar of PAQ_Man
PAQ_Man
Flag of United States of America image

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