tags:

views:

85

answers:

3

I am not quite sure how to draw a Rectangle (not filled) when I drag my mousedown while left clicking the mouse.

I have this so far

            private void canevas_MouseDown( object sender , MouseEventArgs e )
            {
                    if( e.Button == MouseButtons.Left )
                    {
                            _topLeft = new Point( e.X , e.Y );
                            _drawing = true;
                    }
            }

            private void canevas_MouseMove( object sender , MouseEventArgs e )
            {
                    if( _drawing )
                    {
                            Rectangle rec = new Rectangle( _topLeft.X , _topLeft.Y , ( e.X - _topLeft.X ) , ( e.Y - _topLeft.Y ) );
                            canevas.CreateGraphics().DrawRectangle( Pens.Black , rec );
                    }
            }

But the problems it that I dont want all the rectangles to show up

+4  A: 

Don't call CreateGraphics. In MouseDown, store the starting position and a flag to indicate that you are drawing. In MouseMove, check the flag. If you are drawing, create the rectangle relative to the starting position and store it (which you are already doing), and then call Invalidate(). All of your drawing code will be in OnPaint() (canvas.Paint from outside the class, though I would probably create my own class for this to avoid littering your form code with this stuff).

Drawing should be done in your paint handler (OnPaint). If you draw outside of that, your graphics object is not cleared (hence the multiple rectangles) and anything you draw to it can/will be wiped out at seemingly odd times when your window receives a WM_PAINT message.

EDIT: Now that you are having performance problems, there are a couple of simple ways to optimize the painting a bit. First, Invalidate will optionally take a Rectangle as its argument so that you don't have to repaint the entire control. Secondly, if you are drawing on MouseMove you are going to be drawing quite a bit. Using double buffering will help a lot too, just set the DoubleBuffered property to true or add it to the ControlStyles value by calling SetStyle on the control.

Ed Swangren
btw, I am not quite sure what is invalidate for
Burnzy
@Burnzy, Invalidate innvalidates the graphics of the control so that the paint operation is invoked again by windows. (Basically once the control is invalidated it needs to redraw itself)
Pop Catalin
any idea how to fix the flickering issue?
Burnzy
I added some tips. Also realize that, if you do call CreateGraphics (which you should not be doing here anyway), you should also be calling Dispose() on the returned Graphics object.
Ed Swangren
Thanks for the tip, but why should I do that?
Burnzy
Because the Graphics class implements IDisposable, which tells you as the client that it maintains references to native resources and that it needs to release them before going out of scope. Relying on the finalizer is non-deterministic, so it's best to wrap the declaration/assignment in a 'using' block.
Ed Swangren
A: 

Alright now I have this, its working but, it flickers alot, even with double buffering.

            private void canevas_MouseDown( object sender , MouseEventArgs e )
            {
                    _topLeft = new Point( e.X , e.Y );
                    if( e.Button == MouseButtons.Left )
                    {
                            _topLeft = new Point( e.X , e.Y );
                            _drawing = true;
                    }
            }

            private void canevas_MouseUp( object sender , MouseEventArgs e )
            {
                    _drawing = false;
                    _bottomRight = new Point( e.X , e.Y );
                    int newX = _topLeft.X - (_bottomRight.X - _topLeft.X) / 2;
                    int newY =_topLeft.Y + (_bottomRight.Y - _topLeft.Y) / 2;
                    MouseEventArgs me = new MouseEventArgs( MouseButtons.Left , 1 , newX , newY , 0 );

                    canevas_MouseClick( canevas , me );
            }

            private void canevas_MouseMove( object sender , MouseEventArgs e )
            {
                    if( _drawing )
                    {
                            _bottomRight = new Point( e.X , e.Y );
                            canevas.Invalidate();
                    }
            }

And then on paint

            private void canevas_Paint( object sender , PaintEventArgs e )
            {
                     Graphics g = canevas.CreateGraphics();
                     g.DrawImage( _buffer , new Rectangle( 0 , 0 , canevas.Width , canevas.Height ) );
                     g.DrawRectangle( Pens.White , new Rectangle( _topLeft.X , _topLeft.Y , ( _bottomRight.X - _topLeft.X ) , ( _bottomRight.Y - _topLeft.Y ) ) );
            }
Burnzy
*Don't* use CreateGraphics(). Use e.Graphics in the Paint event.
Hans Passant
+1  A: 

Some code to go with Ed's correct answer:

    Point startPos;      // mouse-down position
    Point currentPos;    // current mouse position
    bool drawing;        // busy drawing
    List<Rectangle> rectangles = new List<Rectangle>();  // previous rectangles

    private Rectangle getRectangle() {
        return new Rectangle(
            Math.Min(startPos.X, currentPos.X),
            Math.Min(startPos.Y, currentPos.Y),
            Math.Abs(startPos.X - currentPos.X),
            Math.Abs(startPos.Y - currentPos.Y));
    }

    private void canevas_MouseDown(object sender, MouseEventArgs e) {
        currentPos = startPos = e.Location;
        drawing = true;
    }

    private void canevas_MouseMove(object sender, MouseEventArgs e) {
        currentPos = e.Location;
        if (drawing) canevas.Invalidate();
    }

    private void canevas_MouseUp(object sender, MouseEventArgs e) {
        if (drawing) {
            drawing = false;
            var rc = getRectangle();
            if (rc.Width > 0 && rc.Height > 0) rectangles.Add(rc);
            canevas.Invalidate();
        }
    }

    private void canevas_Paint(object sender, PaintEventArgs e) {
        if (rectangles.Count > 0) e.Graphics.DrawRectangles(Pens.Black, rectangles.ToArray());
        if (drawing) e.Graphics.DrawRectangle(Pens.Red, getRectangle());
    }

To get a 'canevas' that has double-buffering turned on, so it painting doesn't flicker, use Project + Add New Item, select "Class" and paste this code:

using System;
using System.Windows.Forms;

class Canvas : Panel {
    public Canvas() {
        this.DoubleBuffered = true;
        this.SetStyle(ControlStyles.ResizeRedraw, true);
    }
}

Compile. Drag the new control from the top of the toolbox onto your form, replacing the original 'canevas'. Update the event handlers accordingly.

Hans Passant
thanks, I had it correctly but it helped me with the reverted drag. :)
Burnzy
Everything seems ok so far, except the flickering, any ideas?
Burnzy
I left a comment, are you using e.Graphics in the Paint event? I tested this code, it is flicker-free even without double-buffering.
Hans Passant
yes i am, however, the flickering might depend on the background. It wont flicker if its a play gray, but otherwise it does. EDIT nvm, it still flickers even when background is plain simple
Burnzy
What the heck is a "play gray"? And what's in the background?
Hans Passant
Lol, sorry I meant plain gray. The background is a MandelBrot fractal, however, it still flickers if there is no background.
Burnzy
Post updated with a non-flickering canevas. Below the line.
Hans Passant