tags:

views:

1128

answers:

6

First off, I'm a real beginer at C# so please be gentle.

I'm trying to have a circle follow my cursor. I don't want any "trails" to be left behind.

private void Form1_MouseMove(object sender, MouseEventArgs e)
{

    drawCircle(e.X, e.Y);

}

private void drawCircle(int x, int y)
{
    Pen skyBluePen = new Pen(Brushes.DeepSkyBlue);
    Graphics graphics = CreateGraphics();
    graphics.DrawEllipse(
        skyBluePen, x - 150, y - 150, 300, 300);
    graphics.Dispose();
    this.Invalidate();
}

This works ok, as it draws it and centers on the mouse for every mouse move. However, the "this.Invalidate();" is wrong. It "undraws" the shape after every movement, so I can only see glimpses of it. However, not including it causes every single drawn circle to remain on screen.

How do I get one circle to "gracefully" follow my mouse around, without being too jumpy and without retaining all of the past circles?

+1  A: 

You need to invalidate the form BEFORE you draw your circle.

I'm positive that there are more efficient ways to do this using double-buffering, but I don't have an example off the top of my head.

Chris Thompson
Doesnt make a difference. Gives the same flickering effect.
Chalkey
+6  A: 

You can do something like this:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        Point local = this.PointToClient(Cursor.Position);
        e.Graphics.DrawEllipse(Pens.Red, local.X-25, local.Y-25, 20, 20);
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        Invalidate();
    }
}

Basically, on the mouse move, invalidate. On the Paint, draw your circle.

Erich Mirabal
You can add "this.DoubleBuffered = true;" as part of the ctor, and it might also help with some of the flickering.
Erich Mirabal
Is this all I would need? I've pasted it in and it doesn't actually do anything when I Run besides bring up the form/window...
cksubs
Well, you have to connect the events. I left that part out for brevity.You can either add them on the property editor or add them in the ctor with something like this.Paint += Form1_Paint; and this.MouseMove += Form1_MouseMove;
Erich Mirabal
public Form1() { InitializeComponent(); Paint += Form1_Paint; this.MouseMove += Form1_MouseMove; }Could you just explain how those two bits in the constructor work?
cksubs
The "+=" appends a delegate (i.e. type-safe "callback") to the event. There could be multiple delegates attached, so you have to do a += or -= to detach. This link talks about Creating Events for Windows Forms: http://msdn.microsoft.com/en-us/library/dacysss4.aspx That's just a very basic starting point.
Erich Mirabal
A: 

You don't generally want to do any drawing outside of the paint handler because whenever the paint handler does execute (which could be at any time) it will overwrite whatever you did.

There are a bunch of things that you will have to think about eventually (like what happens when the mouse goes outside of your form, but this should get you started.

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

class C:Form
{
static void Main(){Application.Run(new C());}

private Point? _MousePosition = null;

protected override void OnMouseMove(MouseEventArgs e) {
 _MousePosition = e.Location;
 this.Invalidate();
}

protected override void OnPaint(PaintEventArgs e) {
 if(_MousePosition.HasValue) {
  using(Pen skyBluePen = new Pen(Brushes.DeepSkyBlue)) {
   e.Graphics.DrawEllipse(skyBluePen, _MousePosition.Value.X - 150, _MousePosition.Value.Y - 150, 300, 300);
  }
 }
}
}
why name your class C? that is just weird!
Erich Mirabal
+2  A: 

This works - just tested it...

private int x = 0;
private int y = 0;

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    x = e.X;
    y = e.Y;

    this.Invalidate();
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Pen skyBluePen = new Pen(Brushes.DeepSkyBlue);

    e.Graphics.DrawEllipse(skyBluePen, x - 150, y - 150, 300, 300);

}
Chalkey
A: 

The best way to draw something that is quickly changing is to use a concept known as double buffering. It's really easy to do yourself and you don't have to rely on the double buffering flag. Doing it yourself gives you complete freedom and control.

Basically, instead of drawing on the Form itself, you do all of your drawing on an off-screen Bitmap. You only draw when you know something has changed (in your case, on the mouse move event). You only draw on the screen when you know you have to (after mouse move or when the Paint event is raised).

private void DrawScene(Point mouseLocation)
{
     myGraphics.Clear(Color.White)
     myGraphics.DrawEllipse(skyBluePen, mouseLocation.X - 150, mouseLocation.Y - 150, 300, 300);
     myDrawingSurface.Refresh(); //myDrawingSurface can be a Form or a PictureBox or whatever you'd like.  Normally, you'd only Invalidate areas that have changed
}

private void myDrawingSurface_MouseMove(object sender, MouseEventArgs e)
{
    DrawScene(e.Location);
}

private void myDrawingSurface_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.DrawImage(myBitmap, 0, 0); //Can't remember the exact signature
}

Another "cheating" way of doing it is to simply assign a PictureBox's Image property to the image you are drawing on and call Refresh on the PictureBox. It will handle drawing its image on screen for you. No Paint handler required.

Note. You need to declare myBitmap and myGraphics once. The Bitmap will have to be recreated at the appropriate size when the drawing surface changes size. Also, do NOT keep re-declaring Pens and other graphics objects over and over again. They should be declared once when your program starts. And dispose of them properly when your program is closing.

colithium
A: 

Try adding the following lines in your Form constructor:

this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);

This will tell the Form to only repaint when you tell it to do so. It will also provide double buffering. Good luck!

bufferz