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(ControlStyle s.DoubleBu ffer | ControlStyles.AllPaintingI nWmPaint, 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(_oIma geBackgrou nd, 0, 0);
// Render canvas
if(_oCanvas != null)
_oGraphics.DrawImage(_oCan vas.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.
I have tried everything and no matter what I always get flickers.
I enabled double buffering:
this.SetStyle(ControlStyle
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(_oIma
// Render canvas
if(_oCanvas != null)
_oGraphics.DrawImage(_oCan
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.
ASKER
// Code for refreshing, called by
gr.DrawImage(_oImageBackgr ound, 0, 0);
if(_oCanvas != null)
gr.DrawImage(_oCanvas.Canv as, _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...
gr.DrawImage(_oImageBackgr
if(_oCanvas != null)
gr.DrawImage(_oCanvas.Canv
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...
ASKER
Refresh thread obviously did not make a difference...
Here's my refresh method
public void Render(/*System.Drawing.Gr aphics gr*/)
{
do
{
try
{
if(_bRunning && _oGraphics != null && _oCanvas != null)
{
_oGraphics.DrawImage(_oIma geBackgrou nd, 0, 0); // oImageBackground is a System.Drawing.Bitmap
_oGraphics.DrawImage(_oCan vas.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);
}
Here's my refresh method
public void Render(/*System.Drawing.Gr
{
do
{
try
{
if(_bRunning && _oGraphics != null && _oCanvas != null)
{
_oGraphics.DrawImage(_oIma
_oGraphics.DrawImage(_oCan
if((_iCursor = _iCursor-2) < -Convert.ToInt32(_oCanvas.
_iCursor = Convert.ToInt32(_width);
}
Thread.Sleep(1000 / _iInterval);
}
catch(Exception/* ex*/)
{
//clsUtils.NotifyException
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.
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.
You could add the ControlStyles.UserPaint, and use the Paint event (persistent), instead of using CreateGraphics (non-persistent).
Bob
Bob
ASKER
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(ControlStyle s.DoubleBu ffer | ControlStyles.AllPaintingI nWmPaint |
ControlStyles.AllPaintingI nWmPaint | ControlStyles.UserPaint, true);
this.UpdateStyles();
But it doesn't do anything. Whether I put this or not doesn't change anything.
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(ControlStyle
ControlStyles.AllPaintingI
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(PaintEve ntArgs e)
{
// This section should be left blank - so that it doesn't draw the background..
//base.OnPaintBackground(e );
}
protected override void OnPaintBackground(PaintEve
{
// 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.
ASKER
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.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.