2

I'm using the following code to draw a selection rectangle over a picturebox and allow the user to select and drag it to the desired position.

What I intend to achieve is to allow the user to adjust the rectangle size by implementing option to adjust the rectangle size. Currently I have managed to achieve the following.

How to solve this issue?

enter image description here

public class DraggablePictureBox : PictureBox
{
    Boolean hit1 = false, hit2 = false;
    public Boolean notagimg = true;
    public Boolean editedflag = false;
    public Boolean notext = false;
    public Boolean tdrawflag = false, tdrawflag2 = false;
    Bitmap l;
    public Form1 LaunchOrigin2 { get; set; }
    public Point point = new Point(0, 0);
    public Point point2 = new Point(0, 0);
    public int sizemode = 1;
    public DraggablePictureBox()
    {
        this.Invalidate();
    }
    protected override void OnMouseMove(MouseEventArgs e)
    {
        this.Cursor = Cursors.Arrow;
        if (e.Button == MouseButtons.Left)
        {
            point = e.Location;
            base.OnMouseDown(e);
            this.Invalidate();
        }
    }
    protected override void OnMouseDown(MouseEventArgs e)
    {
        PointF x = new PointF(e.X, e.Y);
        Pen p = new Pen(Brushes.Red, 2f);
        RectangleF rect2 = new RectangleF(1, 1, 1, 1);
        RectangleF rect = new RectangleF(1, 1, 1, 1);
        if (e.Button == MouseButtons.Left)
        {
            this.Cursor = Cursors.Hand;
            //Creating Rectangles to check to find the selected object
            if (notext == false)
            {
                rect = new RectangleF(point, new Size(400,400));
            }
            if (rect.Contains(x) && notext == false)
            {
                hit1 = true;
            }
            if (hit1 == true )
            {
                this.Invalidate();
            }
            this.Invalidate();
        }
    }
    protected override void OnMouseUp(MouseEventArgs e)
    {
        base.OnMouseUp(e);
        tdrawflag = false;
    }
        if (e.Button == MouseButtons.Left)
        {
            point = e.Location;
            base.OnMouseDown(e);
            this.Invalidate();
        }
    }
    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);
        Pen p = new Pen(Brushes.Red, 5);
        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
        Pen p2 = new Pen(Brushes.LightYellow, 5);
        p2.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
        if (!hit1)
        {
            pe.Graphics.DrawRectangle(p, new Rectangle(point, new Size(400, 400)));
        }
        else
        {
            pe.Graphics.DrawRectangle(p2, new Rectangle(point, new Size(400, 400)));
            hit1 = false;
        }
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
techno
  • 6,100
  • 16
  • 86
  • 192
  • a) forget about cropping. What you want is a selection tool. What to do with the selection later doesn't matter yet. b) where is the mousemove event? without it you can't show intermediate steps.. c) for a rubberband example [see here](https://stackoverflow.com/questions/33568377/deleting-drawn-rectangle-zoom-box-after-zooming-in/33569687?r=SearchResults&s=3|24.3310#33569687) or [here](https://stackoverflow.com/questions/25348678/how-do-i-make-that-the-rectangle-first-location-will-be-the-mouse-cursor-locatio/25349796?r=SearchResults&s=5|20.0565#25349796) – TaW Dec 13 '18 at 08:01
  • @TaW I think you should have linked your [Resizable Grid](https://stackoverflow.com/questions/52040555/creating-a-resizable-grid-to-overlay-on-a-image-using-c-sharp?answertab=active#tab-top) as an example (yep, I like that thing :) – Jimi Dec 13 '18 at 08:57
  • Thanks for your kind words. Yup, having an actual persistent control instead of a Rectangle also is an interesting alternative. – TaW Dec 13 '18 at 09:21
  • @TaW I have not implemented those events yet. – techno Dec 13 '18 at 09:30
  • @TaW I'm looking for some arrow or size adjustment at each 4 corners or something similar to how we use the selection tool in photoshop. The initial selection rectangle will be predetermined and the user will only be able to adjust its size. – techno Dec 13 '18 at 09:33
  • @TaW Actually there is a mouse move event.Please see the update. – techno Dec 13 '18 at 10:34
  • OK; I can't analyize the code or test it as I am too tied up. What does and what doesn't work? You will need to detect these cases, either in the mousedown or, better in the mousemove (so you can change the cursor): a) button pressed -> some action. b) inside -> offer movement c) on the border: decide which and offer resize d) outside -> nothing. Maybe the link Jimi offered is helpful, indeed.. – TaW Dec 13 '18 at 10:53
  • @TaW No problem. Please give me an update when you get free time. – techno Dec 13 '18 at 15:45
  • Nah, do try it yourself. Studying the code from jim's link is a good idea, imo. – TaW Dec 13 '18 at 15:57
  • @TaW Please see the update.I'm drawing rectangles at 4 corners to allow the user to click and resize.Now i need to change the cursor when the pointer reaches the corners.The pointer changes only for the bottom right corner,even though the code enters `rect.contains` in other cases. – techno Dec 14 '18 at 03:27

1 Answers1

9

You have different options:

  • You can draw a resizable frame on the picture box
  • You can create a resizable control and add it to picture box

In this answer, I've taken the second option to be able to use built-in sizing features of the controls. Here is a screen capture which shows how it looks like in action:

enter image description here

Example - Creating a Frame Control

As an example, I'll create a resizable control and will add it to the picture box.

using System;
using System.Drawing;
using System.Windows.Forms;
public class FrameControl : Control
{
    public FrameControl()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        DoubleBuffered = true;
        ResizeRedraw = true;
        BackColor = Color.Transparent;
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        using (var p = new Pen(Color.Black, 4))
        {
            p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
            e.Graphics.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
        }
    }
    const int WM_NCHITTEST = 0x84;
    const int WM_SETCURSOR = 0x20;
    const int WM_NCLBUTTONDBLCLK = 0xA3;
    protected override void WndProc(ref Message m)
    {
        int borderWidth = 10;
        if (m.Msg == WM_SETCURSOR)  /*Setting cursor to SizeAll*/
        {
            if ((m.LParam.ToInt32() & 0xffff) == 0x2 /*Move*/)
            {
                Cursor.Current = Cursors.SizeAll;
                m.Result = (IntPtr)1;
                return;
            }
        }
        if ((m.Msg == WM_NCLBUTTONDBLCLK)) /*Disable Mazimiz on Double click*/
        {
            m.Result = (IntPtr)1;
            return;
        }
        base.WndProc(ref m);
        if (m.Msg == WM_NCHITTEST)
        {
            var pos = PointToClient(new Point(m.LParam.ToInt32() & 0xffff,
                m.LParam.ToInt32() >> 16));
            if (pos.X <= ClientRectangle.Left + borderWidth &&
                pos.Y <= ClientRectangle.Top + borderWidth)
                m.Result = new IntPtr(13); //TOPLEFT
            else if (pos.X >= ClientRectangle.Right - borderWidth &&
                pos.Y <= ClientRectangle.Top + borderWidth)
                m.Result = new IntPtr(14); //TOPRIGHT
            else if (pos.X <= ClientRectangle.Left + borderWidth &&
                pos.Y >= ClientRectangle.Bottom - borderWidth)
                m.Result = new IntPtr(16); //BOTTOMLEFT
            else if (pos.X >= ClientRectangle.Right - borderWidth &&
                pos.Y >= ClientRectangle.Bottom - borderWidth)
                m.Result = new IntPtr(17); //BOTTOMRIGHT
            else if (pos.X <= ClientRectangle.Left + borderWidth)
                m.Result = new IntPtr(10); //LEFT
            else if (pos.Y <= ClientRectangle.Top + borderWidth)
                m.Result = new IntPtr(12); //TOP
            else if (pos.X >= ClientRectangle.Right - borderWidth)
                m.Result = new IntPtr(11); //RIGHT
            else if (pos.Y >= ClientRectangle.Bottom - borderWidth)
                m.Result = new IntPtr(15); //Bottom
            else
                m.Result = new IntPtr(2); //Move
        }
    }
}

Then add the control to the picture box:

var s = 100;
var c = new FrameControl();
c.Size = new Size(s, s);
c.Location = new Point((pictureBox1.Width - s) / 2, (pictureBox1.Height - s) / 2);
pictureBox1.Controls.Add(c);

To add a fancy effect of filling outside of the frame with semi-transparent color:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.ExcludeClip(pictureBox1.Controls[0].Bounds);
    using (var b = new SolidBrush(Color.FromArgb(100, Color.Black)))
        e.Graphics.FillRectangle(b, pictureBox1.ClientRectangle);
}

As you can see in the paint event, you can find the FrameControl using pictureBox1.Controls[0]. So you can find its location and size.

You can encapsulate all the logic of the picture box in a derived picture box.

Note: Flicker-free rendering

If you experience flickering when moving the frame, use the following code in your form:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED       
        return cp;
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Thanks a lot.This is exactly what i was looking for.There seems to be a small bug in your code,The size adjustment option is not correct at the top right corner of the image. – techno Dec 14 '18 at 06:17
  • You're welcome. Make sure you have used the latest code. I'll check it as well. – Reza Aghaei Dec 14 '18 at 06:18
  • Just one more thing.I need to identify the location and size of the selected area,how can i achieve this. – techno Dec 14 '18 at 06:20
  • Fixed the adjustment. Also read the last paragraph about size and location of the frame control. – Reza Aghaei Dec 14 '18 at 06:25
  • I have accepted the answer.How can i change the cursor to move cursor when the user tries to move the control? I have tried the following on mousemove event of picturebox` if( pictureBox1.Controls[0].Bounds.Contains(e.Location)) { this.Cursor = Cursors.SizeAll; } ` But this does not seem to work. – techno Dec 14 '18 at 11:55
  • @techno You will like may answer [here](https://stackoverflow.com/a/33482233/3110834). – Reza Aghaei Dec 14 '18 at 15:59
  • Thanks... Can you please take a look at this problem when the zoom mode is set https://stackoverflow.com/q/53800328/848968 – techno Dec 16 '18 at 07:41
  • About setting the cursor, let me know if you have any question about the [linked post](https://stackoverflow.com/a/33482233/3110834) or if you find it useful. – Reza Aghaei Dec 16 '18 at 09:04
  • I did not see your last comment.I'm checking it out now. – techno Dec 26 '18 at 03:51
  • It works just fine :) But the `ButtonDoubleClick` event still maximizes the control inspite handling the `MouseDoubleClick` event. – techno Dec 26 '18 at 04:00
  • Are you sure you have handled `WM_NCLBUTTONDBLCLK` message? – Reza Aghaei Dec 26 '18 at 04:08
  • Yeah,just like in your code.Removed this line though `base.WndProc(ref m);` – techno Dec 26 '18 at 04:14
  • The linked post is working properly, i double checked it. For your case I also edited the answer and put message handling code in a correct order. – Reza Aghaei Dec 26 '18 at 04:34
  • I'm encountering some new issues.Kindly check the update. – techno Mar 09 '19 at 06:44
  • @techno Please consider posting a new question as the current updates has nothing to do with the original question. The original question is asking about the frame and the frame control is doing well in showing the frame. The fancy background is just an add-on in the answer. – Reza Aghaei Mar 09 '19 at 12:35
  • It would be great if you do so, because it's making the Q/A confusing :) – Reza Aghaei Mar 09 '19 at 12:57
  • However, please consider a [MCVE] in your new question as I don't have any problem with the original answer. – Reza Aghaei Mar 09 '19 at 12:58
  • will do shortly. – techno Mar 09 '19 at 13:00
  • I have updated the question with the complete example.Please take a look. – techno Mar 09 '19 at 18:51
  • Opps, I just saw your new question! – Reza Aghaei Mar 09 '19 at 18:56