0

I'm creating a Graphical Editor. I'm able to draw lines and rectangles but now I want to move them, so I am trying to add the MouseMove event now. I tried the following things:

rectangle.MouseMove += shape_MouseMove;

Error:

'System.Drawing.Rectangle' does not contain a definition for 'MouseDown' and no extension method 'MouseDown' accepting a first argument of type 'System.Drawing.Rectangle' could be found (are you missing a using directive or an assembly reference?)

rectangle += shape_MouseMove;

Errors:

Error 2 Cannot convert method group 'shape_MouseMove' to non-delegate type 'System.Drawing.Rectangle'. Did you intend to invoke the method

Error 1 Operator '+=' cannot be applied to operands of type 'System.Drawing.Rectangle' and 'method group'

Code:

private void shape_MouseMove(object sender, MouseEventArgs e)
{

}

private void panel_MouseUp(object sender, MouseEventArgs e)
    {
        draw = false;
        xe = e.X;
        ye = e.Y;

        Item item; 
        Enum.TryParse<Item>(menuComboBoxShape.ComboBox.SelectedValue.ToString(), out item);

        switch (item)
        {

            case Item.Pencil:
                using (Graphics g = panel.CreateGraphics())
                    using (var pen = new Pen(System.Drawing.Color.Black))     //Create the pen used to draw the line (using statement makes sure the pen is disposed)
                    {
                        g.DrawLine(pen, new Point(x, y), new Point(xe, ye));
                    }
                break;
            case Item.Rectangle:
                 Rectangle rectangle = new Rectangle(x, y,xe-x, ye-y);
                 rectangle += shape_MouseMove; //Error here
                 using (Graphics g = panel.CreateGraphics())
                    using (var pen = new Pen(System.Drawing.Color.Black))     //Create the pen used to draw the rectangle (using statement makes sure the pen is disposed)
                    {
                        g.DrawRectangle(pen,rectangle);
                    }
                break;
            default:
                break;
        }
    }

How can I add the MouseMove event to the Rectangle?

Sybren
  • 1,071
  • 3
  • 17
  • 51

3 Answers3

1

You can't solve your problem as it stands.

Rectangle is just a structure, intended to describe some region on the screen. To avoid problems with manual mouse/keyboard handling, you need to wrap shapes into UserControls, and implement shape-specific painting.

This also will use OOP benefits. Instead of switch/case you will get a set of shape types, and every type will be responsible to draw its instances:

public partial class RectangularShape : UserControl
{
    public RectangularShape()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        e.Graphics.FillRectangle(Brushes.Red, ClientRectangle);
    }
}
Dennis
  • 37,026
  • 10
  • 82
  • 150
  • Okay that's clear, I am gonna use `UserControl`s then. – Sybren May 13 '15 at 08:34
  • I have to override the onPaint event, am I right? Do you have an example of a shape wrapped in an `UserControl`? – Sybren May 13 '15 at 08:51
  • @Sybren: `OnPaint` is a method, not an event, but in whole you're right. I've updated the answer. – Dennis May 13 '15 at 09:10
  • UserControls are Controls and will suffer from all the transparency problems that come with these so imo this is a really bad idea. A drawing program will want to freely overlap the draw shapes, mixing simi-transparent colors etc.. How would that work if each shape is a UC? Not to speak of performance..! – TaW May 13 '15 at 09:18
  • @TaW what's your suggestion on how to do it? – Sybren May 13 '15 at 09:21
  • @TaW: manual handling of mouse/keyboard even worse. Moreover, finally OP will create a bunch of his own shape objects to handle drag-n-drop, intersection, etc. Some problems with WinForms controls could be solved by moving to WPF. But drawing all these stuff manually is a hell. I'm supporting the CAD system, where shapes are implemented in their own objects hierarchy. This is a sort of nighmare, because in fact we are re-inventing `Control`s, since we need to handle moving, rotating, scaling and more, more, more manually. – Dennis May 13 '15 at 09:28
  • The degree of 'hell' depends greatly on a) planning and b) the sum of things you want to achieve. Drawing won't be a problem really until you go from a few hundred shapes to many tens of thousands. But then the whole project needs serious planning for that size, with intermediate buffers and groups of shapes, editors and property lists, templates, batch editing etc.. – TaW May 13 '15 at 09:34
  • _manual handling of mouse/keyboard even worse_ How? And: how else?? – TaW May 13 '15 at 09:36
  • @TaW: I mean the way, when you need to dispatch mouse/keyboard events from the control, which is drawing surface, to your own shape types, which are not `Control`s. When the shape is a `Control`, it is much easier. – Dennis May 13 '15 at 09:40
  • Well, I'm not sure, but I haven't seen you app.. Identifying all the shapes that are under a mouse location from a single list is not harder, maybe even easier that going down through the overlapping controls. I nay case I strongly suggest to display a list of the shapes along with the 'canvas'.. – TaW May 13 '15 at 09:55
0

simple as it is ... you can't

you can however create a subclass of Control or UserControl, and create your own type of rectangle with all that fancy UI support...

DarkSquirrel42
  • 10,167
  • 3
  • 20
  • 31
0

You should create a class Shape with all the info needed to draw itself.

Then you should have a List<Shape> and use the Paint event of the Panel to draw all the Shapes.

Using Controls, no matter which, will always run into the tranparency problems inherent to Winforms GDI+ drawing. This means that overlapping controls will not be able to fake transparency well.. Real transparency between controls is not possible at all and faked transparency only works between nested controls.

The Shape class would contain fields for its types, the location and size, colors and penstyle, text and font and maybe many more things your various shapes need to know for drawing themselves.

To edit, change, delete those shapes you will need to process the shapes list and upon MouseDown find the one that was hit; then you can follow it up with MouseMove or enter changing values for the other properties..

How to do that precisely is always a matter of taste; commercial programs have rather different methods to resolve stacking conflicts: Some pick a shape when you click near its border, other cycle though overlapping shapes with each click..

You should also plan for ways to change the z-order of the shapes..

Have fun!

TaW
  • 53,122
  • 8
  • 69
  • 111
  • Shape type can be an enum right? How do I handle the case when an rectangle is clicked it doesn't have the MouseMove event? – Sybren May 13 '15 at 09:36
  • Yes. See [here](http://stackoverflow.com/questions/28714411/update-a-drawing-without-deleting-the-previous-one/28716887?s=3|0.3172#28716887) for another post describing such a class! There I chose a char for convenience but an enum is obvioulsy much better as it will be readable.. – TaW May 13 '15 at 09:39
  • You get the e.location of your panel and then search through the list of shapes; like any other moving code you store the clicked 'object', in our case the shape and change its coordinates in the mousemove, triggering the Invalidate for the Panel. - Later, with many shapes this may get slow. In that case you can add __one__ editing Panel and move it until MouseUp and only then modify the shape.. – TaW May 13 '15 at 09:46