简体   繁体   中英

Drag and drop rectangles in list

I have made a simple app in c# that lets me draw a rectangle and than move it by mouse. Now I want to draw multiple rectangles I also add them to a list, this also works, but I want to be able to move each rectangle by itself. This goes wrong. I can only move the first rectangle I created. If I try to move the other rectangle the first rectangle teleports to my mouse but only if I click on the second rectangle, if i click anywhere else it crashes with a nullpointer (I know how to fix this but its not the problem) What I cant figure out is why cant I move the second rectangle

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace TekenTest
{
    public partial class Form1 : Form
    {
        bool isMouseDown;
        List<Item> _Items;
        Item i;

        public Form1()
        {
            InitializeComponent();
            _Items = new List<Item>(); 
            isMouseDown = false;
        }

        private void tekenVel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            foreach (Item i in this._Items)
            {
                i.drawItem(g);
            }
        }

        private void tekenVel_MouseDown(object sender, MouseEventArgs e)
        {
           this.i = _Items.Find(Item => ((i.X <= e.X && (i.WIDTH + i.X) >= e.X) &&
                                         (i.Y <= e.Y && (i.HEIGTH + i.Y) >= e.Y)));

            i.note = Color.Azure;
            isMouseDown = true;
        }

        private void tekenVel_MouseMove(object sender, MouseEventArgs e)
        {
            if (isMouseDown == true)
            {
                i.X = e.X;
                i.Y = e.Y;
                Refresh();
            }
        }

        private void tekenVel_MouseUp(object sender, MouseEventArgs e)
        {
            isMouseDown = false;
        }

        private void itemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.i = new Item();
            this._Items.Add(i);
            this.Refresh();
        }
    }
}

Object class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TekenTest
{
    class Object
    {
        public int X
        {
            get;
            set;
        }

        public int Y
        {
            get;
            set;
        }

        public int HEIGTH
        {
            get;
            set;
        }

        public int WIDTH
        {
            get;
            set;
        }

        public Object()
        {

        }
    }
}

Item class

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TekenTest 
{
    class Item : Object
    {
        public Rectangle rect;
        public String text;
        public Font font;
        public Brush textb;
        public Color note;

        public Item()
        {
            this.X = 200;
            this.Y = 200;
            this.WIDTH = 200;
            this.HEIGTH = 200;
            font = new Font("Arial", 12, FontStyle.Bold, GraphicsUnit.Point);
            text = "Ik ben tekst";
            note = Color.Yellow;
            textb = Brushes.Black;
        }

        public void drawItem(Graphics g)
        {
            this.rect = new Rectangle(X, Y, WIDTH, HEIGTH);
           // g.DrawRectangle(new)
            g.FillRectangle(new SolidBrush(note), rect);
            g.DrawString(text, font, textb, rect);
        }
    }
}

I would suggest a different approach. I would not make the Item a class variable or a property. Every control has a Property named Tag of type object. It can be used for any purposes. What I would typically do is to upon creation set the item as the tag of the represented control. Then In the Move triggers I would handle extract the Item from the Tag property, cast it from object and then manipulate it directly via values.

private void itemToolStripMenuItem_Click(object sender, EventArgs e)
{
    var i = ((Control)sender.Tag) as Item;

    this.i = new Item();
    this._Items.Add(i);

    // you dynamically create a control and set the Tag property
    someControl.Tag = i;

    this.Refresh();
}


private void tekenVel_MouseDown(object sender, MouseEventArgs e)
{
    var i = ((Control)sender.Tag) as Item;
    if(i!=null)
    {
        i.note = Color.Azure;
        isMouseDown = true;
    }
}

private void tekenVel_MouseMove(object sender, MouseEventArgs e)
{
    if (isMouseDown == true)
    {
            i.X = e.X;
            i.Y = e.Y;
            Refresh();
    }
}

You have probably the problem that the instance is not being found by the find method and you are trying to manipulate a null object. This way this is always going to work on a specific object and you you do not have to search for it. It cleans up the code and it runs much smoother.

EDIT Also, I would Suggest renaming your class from Object to something else. just so it does not get confused with the .NET object root type

Problem can be because of in function tekenVel_Paint in foreach loop you are using name for Item the same as for instance variable i . Change it to other like here:

    private void tekenVel_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;

        foreach (Item obj in _Items)
        {
            obj.drawItem(g);
        }
    }

And similar problem in tekenVel_MouseDown . You also should change name in conditional statement in function Find .

    private void tekenVel_MouseDown(object sender, MouseEventArgs e)
    {
       this.i = _Items.Find(item => ((item.X <= e.X && (item.WIDTH + item.X) >= e.X) &&
                                     (item.Y <= e.Y && (item.HEIGTH + item.Y) >= e.Y)));

        i.note = Color.Azure;
        isMouseDown = true;
    }

Also you don't need isMouseDown variable. You can check if mouse button is pressed using MouseEventArgs e in tekenVel_MouseMove function. On MouseUp event you should set usual color for your active item i and set it to null . And check in OnMouseMove event if i is not null in case if user click inside your control but not inside any object.

I fixed by changing my List searching method. I now the for each loop isnt the best way to go so I will change that later:

        private Item selectItem(MouseEventArgs e)
    {

        IEnumerable<Item> itemQuerry =
        from it in _Items
        where it.X <= e.X && it.WIDTH + it.X >= e.X && it.Y <= e.Y && it.HEIGTH + it.Y >= e.Y
        select it;

        foreach (Item foundItem in itemQuerry)
        {
            this.mItem = foundItem;
        }

        mItem.note = Color.Azure;
        return mItem;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM