3
votes

I am working on a little application in which I draw rectangles and move them around with the mouse.

I have some logic which has some bugs inside and I can't find them.

The first click and drag always puts the rectangle on position around (0,0), and the second click and drag will work just fine.

The third click will again put rectangle on (0,0), and with the fourth click I can drag it again.

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 Pr
{
    public partial class Form1 : Form
    {
        int save=0;
        Timer timer1 = new Timer();
        private Point MouseDownLocation;

        private List<Rectangle> rectangles; 

        public Form1()
        {
            InitializeComponent();
            rectangles = new List<Rectangle>();
            panel1.Paint += panel1_Paint;
            this.DoubleBuffered = true;
            timer1.Interval = 10;
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            Refresh();
        }

        public void PopulateTable(int x, int y)
        {
            rectangles.Add(new Rectangle (500, 10, x, y));
            panel1.Invalidate();
        }

        void panel1_Paint(object sender, PaintEventArgs e)
        {
            foreach( var rectangle in rectangles)
            {
                using (var b=new SolidBrush(Color.HotPink))
                {
                    e.Graphics.FillRectangle(b, rectangle);
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Maximized;
            this.MinimumSize = this.Size;
            this.MaximumSize = this.Size;
        }

        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                for (var i = 0; i < rectangles.Count; i++)
                {
                    if (Cursor.Position.X >= rectangles[i].X  &&
                        Cursor.Position.X <= rectangles[i].X + rectangles[i].Width &&
                        Cursor.Position.Y >= rectangles[i].Y  &&
                        Cursor.Position.Y <= rectangles[i].Y + rectangles[i].Height)
                    {
                        save = i;
                        Rectangle rect = rectangles[save];
                        rect.X = e.X - MouseDownLocation.X; 
                        rect.Y = e.Y - MouseDownLocation.Y;

                        rectangles[save] = rect;
                        panel1.Invalidate();
                        break;
                    }
                }
            }
        }

        protected void panel1_OnMouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                MouseDownLocation = e.Location;
        }

        private void panel2_Paint(object sender, PaintEventArgs e)
        {
        }

        private void label1_Click(object sender, EventArgs e)
        {
        }

        private void button1_Click(object sender, EventArgs e)
        {
            PopulateTable(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text));
        }
    }
}

I bet my moving logic isn't so fine, so i hope someone will bring some idea.

EDIT: I just changed

rect.X = e.X - MouseDownLocation.X; 
rect.Y = e.Y - MouseDownLocation.Y;

to

rect.X = e.X; 
rect.Y = e.Y;

It works better, but I have one problem.

When I click, the rectangle moves so that upper left point of rectangle is at the mouse position.

I want it so that the rectangle stays at the same position, unless I move it.

EDIT 2

I changed part of code to:

rect.X = rect.X + e.X - MouseDownLocation.X;
rect.Y = rect.Y + e.Y - MouseDownLocation.Y;

But, now the rectangle moves too fast, it goes much faster than the mouse.

Could that be problem in timer?

EDIT 3

MouseDownLocatioon must be the problem. If code without it works (with rectangle moving on mouse coordinates), that means something's wrong with that.

Tried to do:

 protected void panel1_OnMouseDown(object sender, MouseEventArgs e)
       {
           if (e.Button == MouseButtons.Left)
               MouseDownLocation = panel1.PointToScreen(e.Location);
       }

Didn't help.

Tried to debug, looks like Y coordinate makes problem. It just makes too big offset in Y coordinate.

I did a mouse movement from left to right (only X coordinate), and debugging showed me this:

rect.X = 500

rect.Y = 100

e.X = 848

e.Y = 216

MouseDownLocation.X = 850

MouseDownLocation.Y = 238

That means that difference for such move is for x only 2, and for y 22!

EDIT 4: Managed to solve it!

Just have to add two lines of code:

MouseDownLocation.X = e.X;
MouseDownLocation.Y = e.Y;

Because, if i held down mouse button, it wouldn't refresh new MouseDownLocation.

1
Do note that Rectangle has (among others) a nice Contains(Point) function ! Also note that Cursor.Position is in Screen coordinates, not the relative ones you get in Mousexxxevents! There are conversion function for both directions: Control.PointToClient and Control.PointToScreenTaW
Thanks, @TaW on your comment. That's why i put panel on (0,0), so i don't have to changeomicito
Screen really is Screen, not Form! And better do it really right instead of twisting your layout ;-)TaW
Yeah, you are probably right :) I will do that, thanks :)omicito

1 Answers

1
votes
rect.X = e.X - MouseDownLocation.X; 
rect.Y = e.Y - MouseDownLocation.Y;

I'm not positive what MouseDownLocation is, but based on the name, when you first start moving the box, the current location (e.X,e.Y) is equal to MouseDownLocation, meaning your math works out to (0,0). This appears to be why you move to the corner.

Technically, you're 2nd and 4th clicks are also doing this, but since you're ALREADY in the corner, it's isn't obvious.

Edit regarding your edit that the rectangle now moves the corner to your cursor: When you click the mouse, you'll need to calculate and store the offset from the rectangles location to the mouse, then use that offset in the MouseMove event handler.