C# draw a moving particle - c#

I am extremely new to c# and I have a simple question:
I am supposed to draw a white particle (rectangle) on a black background and move it horizontally from one of the screen to another.
I did it but the problem is it blinks too much (i.e. it is not smooth even when the speed is high, I can easily see the black background between each move and another)
t.Interval = 1000 / speed;
t.Tick += new EventHandler(t_Tick);
t.Start();
....
void t_Tick(object sender, EventArgs e)
{
//g.Clear(Color.Black);
g.DrawRectangle(new Pen(Brushes.Black, 20), r); //draw a black rectangle in the old position...20 is the thickness of the pen
r.X += move_x;
g.DrawRectangle(new Pen(Brushes.White, 20), r); //draw a white rectangle in the new position...20 is the thickness of the pen
if (r.X >= 1700) ///this means it reached the end of the screen
t.Stop();
}
I used g.Clear to clear the graphics but this also did not work, so I drew a black rectangle in the old position before moving it to the new position.
Any Idea how to remove this blinking or even do it in another way?

Try this out...add a Panel (panel1) to a form:
public partial class Form1 : Form
{
private Rectangle r;
private const int rSize = 50;
private const int move_x = 10;
private System.Windows.Forms.Timer tmr;
public Form1()
{
InitializeComponent();
panel1.BackColor = Color.Black;
r = new Rectangle(0, panel1.Height / 2 - rSize / 2, rSize, rSize);
tmr = new System.Windows.Forms.Timer();
tmr.Interval = 50;
tmr.Tick += new EventHandler(tmr_Tick);
tmr.Start();
panel1.Paint += new PaintEventHandler(panel1_Paint);
}
void tmr_Tick(object sender, EventArgs e)
{
r.X += move_x;
panel1.Refresh();
if (r.X > panel1.Width)
{
tmr.Stop();
MessageBox.Show("Done");
}
}
void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.White, r);
}
}

Related

Drawing characters to form at timer tick

I am trying to draw a character on the screen every 500 milliseconds but they won't appear on the screen
using System;
using System.Drawing;
using System.Windows.Forms;
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
PictureBox pb;
Bitmap surface;
Graphics device;
Timer timer;
int num = 0;
string text = "This is a test";
char[] textChar;
Font font = new Font("Black Ops One", 20, FontStyle.Regular, GraphicsUnit.Pixel);
private void Form4_Load(object sender, EventArgs e)
{
//picture box
pb = new PictureBox();
pb.Parent = this;
pb.Dock = DockStyle.Fill;
pb.BackColor = Color.Black;
//create graphics device
surface = new Bitmap(this.Size.Width, this.Size.Height);
pb.Image = surface;
device = Graphics.FromImage(surface);
//set up timer
timer = new Timer();
timer.Interval = 500;
timer.Enabled = true;
timer.Tick += new System.EventHandler(TimerTick);
//mis
textChar = text.ToCharArray();
}
public void TimerTick(object sender, EventArgs e)
{
DrawText();
if (num > textChar.Length - 1)
{
timer.Enabled = false;
MessageBox.Show("have hit the end");
}
}
public void DrawText()
{
device.DrawString(textChar[num].ToString(), font, Brushes.Red, 10, 10 + num * 22)
num++;
}
}
I hope to have the form at the end have the string on the form, but have the characters come up one by one. The form won't show any of the text. It only shows a black background.
You need to make the bitmap the image of the picture box pb.Image = surface.
public void DrawText()
{
device.DrawString(textChar[num].ToString(), font, Brushes.Red, 10, 10 + num * 22)
num++;
pb.Image = surface;
}

How do I rotate all graphics in form without causing MouseArgs to go haywire

Lately I found out how to rotate images and I already have problem.
Here is piece of code that I have problem with but you probably don't need to look at it anyway...
Graphics g = e.Graphics;
g.TranslateTransform((float)Width / 2, (float)Height / 2);
g.RotateTransform(myAngle);
Brush brush = new SolidBrush(Color.FromArgb(32, 1, 1, 1));
Pen pen = new Pen(Color.FromArgb(255, 128 , 128, 128), 3);
g.FillRectangle(brush, nX, nY, snX - nX, snY - nY);
g.DrawRectangle(pen, nX, nY, snX - nX, snY - nY);
variables X , sX , snX, Y , sY, snY are coordinates of mouse in specific moments and are calculated mostly in Form1_MouseMove and I can't show what's in there.
How can I make these variables also change no matter what myAngle is?
I've made an example, how to draw on a rotated bitmap: (using a picturebox/bitmap/trackbar)
public partial class Form1 : Form
{
private Bitmap _bitmap;
public Form1()
{
InitializeComponent();
_bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
}
private void TransformGraphics(Graphics g)
{
g.ResetTransform();
g.TranslateTransform(_bitmap.Width / 2, _bitmap.Height / 2);
g.RotateTransform(trackBar1.Value);
g.TranslateTransform(-_bitmap.Width / 2, -_bitmap.Height / 2);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
TransformGraphics(e.Graphics);
e.Graphics.DrawImage(_bitmap, new Point());
}
private Point? _previousPoint;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
_previousPoint = e.Location;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
_previousPoint = null;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (_previousPoint.HasValue)
{
using (Graphics g = Graphics.FromImage(_bitmap))
{
TransformGraphics(g);
var matrix = g.Transform;
matrix.Invert();
var points = new[] { _previousPoint.Value, e.Location };
matrix.TransformPoints(points);
g.ResetTransform();
g.DrawLine(Pens.Black, points[0], points[1]);
pictureBox1.Invalidate();
_previousPoint = e.Location;
}
}
}
private void trackBar1_ValueChanged(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}
}
You can add another trackbar for scaling.

Winforms Graphics flickering. (Double buffering doesn't help!)

I'm trying to create a simple Windows Forms graphics app that basically will draw a circle every time the user clicks and it expands, while slowly fading away.
When I tried to use the Paint() Event for my graphics functionality, nothing happened, so I created a separate function called "Render" that is called in my main update Timer.
The app worked but the graphics flickered. After some researched I realized that I had to enable Double Buffering so that it would render to a buffer and then the buffer would be rendered to the screen.
The flickering still didn't stop!
Is this because double buffering only works for Paint() events and if so how do I get the Paint() event to work or am I not enabling Double Buffering right?
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Widget
{
public class Circle
{
public float X;
public float Y;
public float Radius;
public int Alpha;
public Circle(float X, float Y, float Radius, int Alpha)
{
this.X = X;
this.Y = Y;
this.Radius = Radius;
this.Alpha = Alpha;
}
}
public partial class Form1 : Form
{
public static readonly int ScreenX = Screen.PrimaryScreen.Bounds.Width;
public static readonly int ScreenY = Screen.PrimaryScreen.Bounds.Height;
public int WindowWidth = 500, WindowHeight = 500;
public Graphics G;
private Pen Pen;
private Timer timer = new Timer();
private List<Circle> Circles;
public Form1()
{
this.Text = "Widget - Sam Brandt";
this.Size = new Size(WindowWidth, WindowHeight);
this.StartPosition = FormStartPosition.Manual;
this.Location = new Point(ScreenX - WindowWidth - 100, 0);
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.Icon = new Icon("C:\\Users\\Admin\\Desktop\\Code Repositories\\Visual Studios\\Widget\\Widget\\Properties\\WidgetIcon.ico");
Pen = new Pen(Color.Black, 1);
G = CreateGraphics();
//this.Paint += new PaintEventHandler(OnPaint);
ConstructMouse();
FormWithTimer();
DoubleBuffered = true;
Circles = new List<Circle>();
}
public void ConstructMouse()
{
this.MouseUp += new MouseEventHandler(OnMouseUp);
this.MouseMove += new MouseEventHandler(OnMouseMove);
this.MouseDown += new MouseEventHandler(OnMouseDown);
}
public void FormWithTimer()
{
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = (10);
timer.Enabled = true;
timer.Start();
}
protected void OnMouseUp(object sender, MouseEventArgs e)
{
}
protected void OnMouseMove(object sender, MouseEventArgs e)
{
}
public void OnMouseDown(object sender, MouseEventArgs e)
{
Circles.Add(new Circle(e.Location.X, e.Location.Y, 0, 255));
}
/*public void OnPaint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
for (int i = 0; i < Circles.Count; i++)
{
Circle C = Circles[i];
e.Graphics.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
}
}*/
private void Tick()
{
for (int i = 0; i < Circles.Count; i++)
{
Circle C = Circles[i];
C.Radius++;
C.Alpha -= 3;
if (C.Alpha == 0)
{
Circles.RemoveAt(i);
}
}
}
public void Render()
{
G.Clear(Color.White);
for (int i = 0; i < Circles.Count; i++)
{
Circle C = Circles[i];
G.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
}
}
public void timer_Tick(object sender, EventArgs e)
{
Render();
Tick();
}
}
}
Short answer - keep DoubleBuffered = true and use Paint event.
When I tried to use a PaintEvent for my graphics functionality, nothing happened
When you do some modifications and want to reflect them, use Control.Invalidate method, which according to the documentation
Invalidates the entire surface of the control and causes the control to be redrawn.
In your case, something like this
void timer_Tick(object sender, EventArgs e)
{
Tick();
Invalidate();
}
More observations here but probably will be the answer.
Why Timer?
Use the Paint event, it is called when the GDI+ determines it needs to, you are constantly painting with your code as-is.
Your code makes it look like you are not using double buffering.
I'd do all your drawing to a separate Graphics object, and then copy that to your main Graphics object on the timer tick event only if there's been a change. You may need to keep track of that with a boolean member. This means your background drawing will have to be triggered by some other event.
If your picture is actually changing every 10 milliseconds, I'd slow down the timer a bit and set it to 50 milliseconds.
Try this (in VB):
Dim aProp = GetType(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
aProp.SetValue(Me, True, Nothing)
Also had problems with double buffering, and usual setting property DoubleBuffered to true do not work. I have found this solution somewhere on web

MouseMove Event in Form

I want to be able to draw Graphics to the Form Window instead of a picturebox. But it doesn't seem that the form window captures the mousemove event.
namespace CollisionTest
{
public partial class Form1 : Form
{
private Graphics paper;
private Pen pen;
public Form1()
{
InitializeComponent();
//paper = pictureBox1.CreateGraphics();
paper = this.CreateGraphics();
pen = new Pen(Color.Blue);
pen.Width = 5;
this.MouseMove += new System.Windows.Forms.MouseEventHandler(Form1_MouseMove);
}
private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
paper.Clear(Form1.ActiveForm.BackColor);
paper.DrawRectangle(pen, e.X + 10, this.Height - 20, 50, 10);
}
}
}
paper.Clear method Clears the entire drawing surface and fills it with the specified background color.
so when you move your mouse you first clear the graphic object and draw something so you cant see anything.
test with removing :
paper.Clear(Form1.ActiveForm.BackColor);
from your code
Looks like you want a "pong paddle" going across the bottom of your form?
Just change this.Height to this.ClientRectangle.Height:
public partial class Form1 : Form
{
private Pen pen;
private Graphics paper;
public Form1()
{
InitializeComponent();
pen = new Pen(Color.Blue, 5);
paper = this.CreateGraphics();
this.MouseMove += new System.Windows.Forms.MouseEventHandler(Form1_MouseMove);
}
private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
paper.Clear(this.BackColor);
paper.DrawRectangle(pen, e.X + 10, this.ClientRectangle.Height - 20, 50, 10);
}
}

winForm invalidate(rectangle)

I'd like to draw animation where the airplane crossing form from leftside to the right.
public partial class Form1 : Form
{
Bitmap sky, plane, background;
int currentX, currentY;
Random rndHeight;
Rectangle planeRect;
Graphics g;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
sky = new Bitmap("sky.jpg");
plane = new Bitmap("plane1.png");
int planeWidth = plane.Width; int planeHeight = plane.Height;
}
catch (Exception) { MessageBox.Show("No files!"); }
this.ClientSize = new System.Drawing.Size(sky.Width, sky.Height);
this.FormBorderStyle = FormBorderStyle.FixedSingle;
background = new Bitmap(sky);
g = Graphics.FromImage(background);
rndHeight = new Random();
currentX = -plane.Width; currentY = rndHeight.Next(0, this.Height);
this.BackgroundImage = background;
timer1.Interval = 1;
timer1.Enabled = true;
timer1.Start();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(sky, 0, 0);
e.Graphics.DrawImage(plane, planeRect);
}
private void timer1_Tick(object sender, EventArgs e)
{
g.DrawImage(sky, 0, 0);
planeRect.X = currentX; planeRect.Y = currentY; planeRect.Width = plane.Width; planeRect.Height = plane.Height;
g.DrawImage(plane, planeRect);
Rectangle myNewPlane = new Rectangle(planeRect.X - 10, planeRect.Y - 10, planeRect.Width + 20, planeRect.Height + 20);
this.Invalidate(myNewPlane);
if (currentX >= this.Width) currentX = -plane.Width; else currentX += 2;
currentY += rndHeight.Next(-2, 2);
}
}
This code works, but the Rectangle of plane flickers with the frequency of timer1.Interval. My question is: how can I avoid these flickers?
p.s.: background image resolution 1024x768; plane - 160x87. plane is transparent
You fix this by setting the DoubleBuffering style for your Form to remove the flicker, e.g.
DoubleBuffered = true;
You probably want to set a few more control styles as well for automatic double-buffering (after InitializeComponents)
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,true);
More information about automatic and manual double-buffering is on MSDN here

Categories