akd
akd

Reputation: 179

How do I paint a ball moving in a panel using WinForms?

I have code that will paint a ball when I click the mouse in my panel. Now, what I want is when I click on the panel, not only does a ball appear, but it also moves at some velocity. Right now I don't really care if the ball travels past the panel's boundaries. How do I do this?

public partial class Form1 : Form
{
    ArrayList dotPts = new ArrayList();

    public Form1()
    {
        InitializeComponent();
    }

    private void mainPanel_Paint(object sender, PaintEventArgs e)
    {
        foreach (Point p in dotPts)
        { 
            e.Graphics.FillEllipse(Brushes.Black, p.X, p.Y, 20, 20); 
        }
    }

    private void mainPanel_MouseUp(object sender, MouseEventArgs e)
    {
        Graphics g = Graphics.FromHwnd(this.Handle);
        dotPts.Add(new Point(e.X - 10, e.Y - 10));
        mainPanel.Invalidate();
    }
}

InitializeComponent():

private void InitializeComponent()
    {
        this.mainPanel = new System.Windows.Forms.Panel();
        this.SuspendLayout();
        // 
        // mainPanel
        // 
        this.mainPanel.BackColor = System.Drawing.SystemColors.GradientInactiveCaption;
        this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
        this.mainPanel.Location = new System.Drawing.Point(12, 12);
        this.mainPanel.Name = "mainPanel";
        this.mainPanel.Size = new System.Drawing.Size(790, 424);
        this.mainPanel.TabIndex = 0;
        this.mainPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.mainPanel_Paint);
        this.mainPanel.MouseUp += new System.Windows.Forms.MouseEventHandler(this.mainPanel_MouseUp);
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(821, 447);
        this.Controls.Add(this.mainPanel);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);

    }

Upvotes: 2

Views: 5907

Answers (3)

Shekhar_Pro
Shekhar_Pro

Reputation: 18430

I have got an example (quick and dirty) code i tried to give you an idea.. (run it in LINQPAD)

using System.Drawing;
using System.Windows.Forms;

void Main()
{
    Application.Run(new Form1());
}

public class Form1 : Form
{
    float time3 = 0, time = 0, time2 = 1000000;
    int a = -1;
    float BallHeight = 20, BallWidth = 20;
    PointF BallLocation = new PointF(0, 0);
    PointF BallVelocity = new PointF(50, 50);
    DateTime dt;
    float x = 0;
    System.Timers.Timer tmr = new System.Timers.Timer();
    System.Timers.Timer gmlp = new System.Timers.Timer();
    public Form1()
    {
        this.Size = new Size(700, 700);
        Label lb = new Label();

        tmr.Interval = 20;
        tmr.Elapsed += (s, e) =>
        {

            //if(BallLocation.X >= 500) BallVelocity.X *= a;
            //if(time3 >= 1000) time=0; else 
            time3 += 20;// (DateTime.Now.Ticks - dt.Ticks)/10000;
            
            BallLocation.X = BallVelocity.X * (time3 / 1000);
            BallLocation.Y = BallVelocity.Y * (time3 / 1000);
            this.Invalidate();
            if (time >= time2) { tmr.Stop(); tmr.Enabled = false; gmlp.Stop(); gmlp.Enabled = false; }
        };
        this.DoubleBuffered = true;


        gmlp.Interval = 1000;
        gmlp.Elapsed += (s, e) =>
        {
            //dt = dt.AddSeconds(1);
            lb.Text =
            "time: " + time +
            "\ntime2: " + time2 +
            "\ntime3: " + time3 +
            "\nlocx: " + BallLocation.X +
            "\ntimespan: " + (DateTime.Now.Ticks - dt.Ticks) / 10000 +
            "\nx moved: " + (BallLocation.X - x);
        };
        gmlp.Enabled = true;
        gmlp.Start();
        tmr.Enabled = true;
        tmr.Start();
        lb.Location = new Point(20, 20);
        lb.Size = new Size(80, 200);
        this.Controls.Add(lb);
        dt = DateTime.Now;
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);
        pe.Graphics.FillEllipse(Brushes.Tomato, 
            new Rectangle((int)BallLocation.X, (int)BallLocation.Y, (int)BallHeight, (int)BallWidth));
    }
}

Upvotes: 1

Hans Passant
Hans Passant

Reputation: 941625

You need a Timer. In the Tick event handler calculate a new position for the object and call the panel's Invalidate() method so it gets repainted. You can use a PictureBox instead of a Panel if the flicker is getting too noticeable.

Work on the ArrayList as well. This needs to become a List<Ball> where the Ball class stores the position as well as the velocity vector. Plus whatever other properties you'll add in the future like color or radius.

Upvotes: 2

bjornars
bjornars

Reputation: 1516

The general solution to get something to move smoothly over time is to make a timer-object to change the position of the ball by the velocity (or some fraction of it) every tick:

  ball.x += xVelocity;
  ball.y += yVelocity;

Upvotes: 0

Related Questions