Hi all.
I'm currently very interested in drawing stuff (like lines and ellipses) on the form with GDI+ and then moving them. But when i was trying to move them (animation) with a timer, they flickered (flashing sometimes).
So, basically i have 2 questions:
1) I've googled long for a flicker-free solution for moving a drawing over the form, but i got no succes (well tried a few examples, but all didn't work well somehow). Do someone here know how to do it in an easy example?
2) This is just a question which will maybe be useful for me at later times: Let's say you have 2 objects, the background (some picture), and the 'sun' (circle drawed and filled using GDI+). Now, while moving the 'sun' around, what is the best way to programmatically do that... i thought, one way is to draw each 'round' or 'tick' (when something should have changed) the whole stuff again (so first draw background, then sun again on new position). This however, will be heavy, so i'm sure there must be a better solution. Anybody?
Thanks for the one who can answer this (especially question 1).

This is what i have currently for testing:
Form1.cs:
CODE
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace PaintTest
{
public partial class Form1 : Form
{
Graphics g;
Circle myCircle;
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
myCircle = new Circle();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
g = e.Graphics;
myCircle.Draw(g);
}
private void Form1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
myCircle.Move(1);
this.Invalidate(false);
}
}
}
Circle.cs:
CODE
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
namespace PaintTest
{
class Circle
{
Rectangle myRectangle;
public Circle()
{
myRectangle = new Rectangle(20, 20, 40, 40);
}
public void Move(int vertmove)
{
myRectangle.Y += vertmove;
}
public void Draw(Graphics g)
{
g.DrawEllipse(new Pen(Color.Blue), myRectangle);
}
}
}
You see, i have already these lines:
CODE
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
to reduce flicker, but it don't help much...
Edit:I tried an example from MSDN, and edited it a bit, so it shows a moving circle... it don't flicker, but it also don't move fluently:
CODE
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace BufferingExample
{
public class Form1 : Form
{
private BufferedGraphicsContext context;
private BufferedGraphics grafx;
private byte bufferingMode;
private string[] bufferingModeStrings =
{ "Draw to Form without OptimizedDoubleBufferring control style",
"Draw to Form using OptimizedDoubleBuffering control style",
"Draw to HDC for form" };
private System.Windows.Forms.Timer timer1;
private byte count;
private Rectangle myRectangle;
public Form1()
: base()
{
// Configure the Form for this example.
this.Text = "User double buffering";
this.MouseDown += new MouseEventHandler(this.MouseDownHandler);
this.Resize += new EventHandler(this.OnResize);
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
// Configure a timer to draw graphics updates.
timer1 = new System.Windows.Forms.Timer();
timer1.Interval = 10;
timer1.Tick += new EventHandler(this.OnTimer);
bufferingMode = 2;
count = 0;
myRectangle = new Rectangle(20, 20, 40, 40);
// Retrieves the BufferedGraphicsContext for the
// current application domain.
context = BufferedGraphicsManager.Current;
// Sets the maximum size for the primary graphics buffer
// of the buffered graphics context for the application
// domain. Any allocation requests for a buffer larger
// than this will create a temporary buffered graphics
// context to host the graphics buffer.
context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
// Allocates a graphics buffer the size of this form
// using the pixel format of the Graphics created by
// the Form.CreateGraphics() method, which returns a
// Graphics object that matches the pixel format of the form.
grafx = context.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.Width, this.Height));
// Draw the first frame to the buffer.
DrawToBuffer(grafx.Graphics);
}
private void MouseDownHandler(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
// Cycle the buffering mode.
if (++bufferingMode > 2)
bufferingMode = 0;
// If the previous buffering mode used
// the OptimizedDoubleBuffering ControlStyle,
// disable the control style.
if (bufferingMode == 1)
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
// If the current buffering mode uses
// the OptimizedDoubleBuffering ControlStyle,
// enabke the control style.
if (bufferingMode == 2)
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
// Cause the background to be cleared and redraw.
count = 6;
DrawToBuffer(grafx.Graphics);
this.Refresh();
}
else
{
// Toggle whether the redraw timer is active.
if (timer1.Enabled)
timer1.Stop();
else
timer1.Start();
}
}
private void OnTimer(object sender, EventArgs e)
{
// Draw randomly positioned ellipses to the buffer.
DrawToBuffer(grafx.Graphics);
myRectangle.Y++;
// If in bufferingMode 2, draw to the form's HDC.
if (bufferingMode == 2)
// Render the graphics buffer to the form's HDC.
grafx.Render(Graphics.FromHwnd(this.Handle));
// If in bufferingMode 0 or 1, draw in the paint method.
else
this.Refresh();
}
private void OnResize(object sender, EventArgs e)
{
// Re-create the graphics buffer for a new window size.
context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
if (grafx != null)
{
grafx.Dispose();
grafx = null;
}
grafx = context.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.Width, this.Height));
// Cause the background to be cleared and redraw.
count = 6;
DrawToBuffer(grafx.Graphics);
this.Refresh();
}
private void DrawToBuffer(Graphics g)
{
// Clear the graphics buffer every five updates.
//if (++count > 5)
//{
// count = 0;
// grafx.Graphics.FillRectangle(Brushes.Black, 0, 0, this.Width, this.Height);
//}
grafx.Graphics.FillRectangle(Brushes.Black, 0, 0, this.Width, this.Height);
// Draw randomly positioned and colored ellipses.
//Random rnd = new Random();
//for (int i = 0; i < 20; i++)
//{
// int px = rnd.Next(20, this.Width - 40);
// int py = rnd.Next(20, this.Height - 40);
// g.DrawEllipse(new Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)), 1),
// px, py, px + rnd.Next(0, this.Width - px - 20), py + rnd.Next(0, this.Height - py - 20));
//}
g.DrawEllipse(new Pen(Color.Red), myRectangle);
// Draw information strings.
g.DrawString("Buffering Mode: " + bufferingModeStrings[bufferingMode], new Font("Arial", 8), Brushes.White, 10, 10);
g.DrawString("Right-click to cycle buffering mode", new Font("Arial", 8), Brushes.White, 10, 22);
g.DrawString("Left-click to toggle timed display refresh", new Font("Arial", 8), Brushes.White, 10, 34);
}
protected override void OnPaint(PaintEventArgs e)
{
grafx.Render(e.Graphics);
}
[STAThread]
public static void Main(string[] args)
{
Application.Run(new Form1());
}
}
}
So it 'shocks' a bit sometimes... is C# really not capable to do it better than this?
This post has been edited by nbarten: 4 Jul, 2008 - 02:09 AM