none
How to add some handles to my rectangular selection in the PictureBox? RRS feed

  • Question

  • Hi all,

    I'm using this code to make a rectangular selection on my picture in my app 

    and I want to make some handles on it as it illustrate  in the picture below.

    Here's the code

    Public Class Form1
        Private SelectSiz As Size
        Private SelectPos As System.Drawing.Point
        Private Selecting As Boolean = False
        Private Selected As Boolean = False
        Private source As Image
    
        Dim XOffs As Integer
        Dim YOffs As Integer
    
      
        Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles PictureBox1.MouseDown
            Selecting = True
            SelectPos = New Point(e.Location)
            SelectSiz = New Size(1, 1)
            Me.Refresh()
        End Sub
    
        Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles PictureBox1.MouseMove
            If Selecting Then
                SelectSiz = New Size(e.Location.X - SelectPos.X, e.Location.Y - SelectPos.Y)
                Selected = True
                Me.Refresh()
            End If
        End Sub
    
        Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles PictureBox1.MouseUp
            Selecting = False
        End Sub
    
    
    
        Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles PictureBox1.Paint
            If Selected Then
                e.Graphics.DrawRectangle(Pens.Black, GetSelection(SelectPos, SelectSiz))
            End If
        End Sub
    
        Public Function GetSelection(ByVal Start As Point, ByVal Range As Size) As Rectangle
            Dim X As Integer = Start.X
            Dim X1 As Integer = Range.Width
            If X1 < 0 Then
                X1 = Math.Abs(X1)
                X -= X1
            End If
            Dim Y As Integer = Start.Y
            Dim Y1 As Integer = Range.Height
    
            If Y1 < 0 Then
                Y1 = Math.Abs(Y1)
                Y -= Y1
            End If
            Return New Rectangle(X, Y, X1, Y1)
        End Function
        Private Sub PictureBox1_MouseWhee(ByVal sender As System.Object, ByVal e As MouseEventArgs) Handles PictureBox1.MouseWheel
            If e.Delta <> 0 Then
                If e.Delta <= 0 Then
                    If PictureBox1.Width < 500 Then Exit Sub 'minimum 500?
                Else
                    If PictureBox1.Width > 2000 Then Exit Sub 'maximum 2000?
                End If
    
                PictureBox1.Width += CInt(PictureBox1.Width * e.Delta / 1000)
                PictureBox1.Height += CInt(PictureBox1.Height * e.Delta / 1000)
            End If
    
        End Sub
        Private Sub PictureBox1_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.MouseEnter
            PictureBox1.Focus()
        End Sub
        Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click
            If Selected = False Then MsgBox("Please select area to crop it first")
            Try
                If Selected Then
                    Dim Cropped As Image = New Bitmap(Math.Abs(SelectSiz.Width), Math.Abs(SelectSiz.Height))
                    Dim g As Graphics = Graphics.FromImage(Cropped)
    
                    Dim SrcRect As Rectangle = GetSelection(SelectPos, SelectSiz)
                    SrcRect.X += XOffs
                    SrcRect.Y += YOffs
                    g.DrawImage(source, New Rectangle(0, 0, Cropped.Width, Cropped.Height), SrcRect, GraphicsUnit.Pixel)
                    PictureBox1.Image = Cropped
                    Button3.Enabled = True
                    Button4.Enabled = True
                    Button7.Enabled = True
                    Button10.Enabled = True
    
    
                End If
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
            Selected = False
        End Sub
       

    And here is the illustrator image with handles


    Thanks advance....

    Saturday, July 8, 2017 1:20 AM

Answers

  • Here is a code example. You will have to modify it for your exact needs.

    'draw rectangle move with mouse 
    Option Strict On
    Public Class DrawMouseMovableRectangle
        Private WithEvents PictureBox1 As New PictureBox With {.Parent = Me, .Dock = DockStyle.Fill, .BackColor = Color.Black}
        Private MouseDownStage As Integer
        Private MouseDownPt, MouseDownOffsetPt, TracerPt As PointF
        Private rect As RectangleF
    
        Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
            MouseDownStage = MouseOverRectangle(e.X, e.Y)
            Select Case MouseDownStage
                Case 1
                    MouseDownPt.X = e.X
                    MouseDownPt.Y = e.Y
                Case 2, 3
                    MouseDownPt.X = rect.X
                    MouseDownPt.Y = rect.Y
                    MouseDownOffsetPt.X = e.X - rect.X
                    MouseDownOffsetPt.Y = e.Y - rect.Y
            End Select
        End Sub
    
        Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
            If MouseDownStage > 0 Then
                Select Case MouseDownStage
                    Case 1
                        TracerPt.X = e.X
                        TracerPt.Y = e.Y
                    Case 2                    'moving fence
                        Dim dx, dy, w, h As Single
                        dx = e.X - MouseDownPt.X
                        dy = e.Y - MouseDownPt.Y
                        rect.X = MouseDownPt.X + dx - MouseDownOffsetPt.X
                        rect.Y = MouseDownPt.Y + dy - MouseDownOffsetPt.Y
                    Case 3                    'moving handle
                        rect.Width = Math.Abs(rect.X - e.X)
                        rect.Height = Math.Abs(rect.Y - e.Y)
                End Select
            Else
                Select Case MouseOverRectangle(e.X, e.Y)
                    Case 2  'fence
                        Cursor.Current = Cursors.Hand
                    Case 3 'handle
                        Cursor.Current = Cursors.SizeNESW
                    Case Else
                        Cursor.Current = Cursors.Default
                End Select
            End If
    
            PictureBox1.Invalidate()
    
        End Sub
    
        Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp
    
            Select Case MouseDownStage
                Case 1
                    rect = New RectangleF(MouseDownPt.X, MouseDownPt.Y, (TracerPt.X - MouseDownPt.X), (TracerPt.Y - MouseDownPt.Y))
            End Select
    
            MouseDownStage = 0
        End Sub
    
        Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    
            With e.Graphics
                Using p As New Pen(Color.White, 1)
                    If rect.Width > 0 Then
                        p.DashStyle = Drawing2D.DashStyle.Dash
                        .DrawRectangle(p, Rectangle.Round(rect))
    
                        p.Width = 2
                        p.Color = Color.LimeGreen
                        p.DashStyle = Drawing2D.DashStyle.Solid
    
                        Dim h As Integer = 5
                        Dim rect2 As New Rectangle(CInt(rect.X + rect.Width - h), CInt(rect.Y + rect.Height - h), 2 * h, 2 * h)
                        .DrawRectangle(p, rect2)
                    End If
    
                    If MouseDownStage = 1 Then
                        p.Color = Color.Yellow
                        p.DashStyle = Drawing2D.DashStyle.Solid
                        .DrawRectangle(p, MouseDownPt.X, MouseDownPt.Y, (TracerPt.X - MouseDownPt.X), (TracerPt.Y - MouseDownPt.Y))
                    End If
                End Using
            End With
        End Sub
    
        Private Function MouseOverRectangle(x As Integer, y As Integer) As Integer
            'determine if the mouse pointer is over the handle or fence
            Dim h As Integer = 5
            Dim handleRect As New Rectangle(CInt(rect.X + rect.Width - h), CInt(rect.Y + rect.Height - h), 2 * h, 2 * h)
            Dim fenceRect As RectangleF = rect
    
            If handleRect.Contains(x, y) Then
                MouseOverRectangle = 3              'mouse over handle
            ElseIf fenceRect.Contains(x, y) Then
                MouseOverRectangle = 2              'mouse over fence rect
            Else
                MouseOverRectangle = 1              'mouse not over anything
            End If
        End Function
    End Class

    Saturday, July 8, 2017 1:00 PM

All replies

  • Hi,

    use a GraphicsPath and add the Rectangles for the handles to it.

    Use the IsVisible method of the GraphicsPath to determine which handle is clicked and resize the shape according to that handle.

    Regards,

      Thorsten

    Saturday, July 8, 2017 1:29 AM
  • Hi,

    use a GraphicsPath and add the Rectangles for the handles to it.

    Use the IsVisible method of the GraphicsPath to determine which handles is clicked and resize the shape according to that handle.

    Regards,

      Thorsten

    How to do that ?

    because I'm kinda beginner in vb.net sorry

    Saturday, July 8, 2017 1:31 AM
  • Here's the code

    You haven't included any code to draw the handles, so it is not clear what the problem is.  You need to show the code that you are using instead of .DrawRectangle that draws the area as dashed lines with the resize points at each corner and in the middle.

    Saturday, July 8, 2017 1:37 AM
  • Hi,

    this is not easy to tell since its highly coupled to the drawing of the shapes andalso to some properties of the shapes.

    - Create a new GraphcisPath, add the current Rectangle to it

    - Create a second GraphicsPath and add the eight handles as rectangles to it

    - [Maybe needed: Transform the paths to place them correctly (eg, if the shape is rotated)]

    - Draw both path-objects with the Graphics-object from your DesignSurface (eg a PictureBox)

    - Implement HitTesting for the handles (triggered in mouseDown)

    - Implement the change of [Location and] Size of your shapes by (Mouse-) moving the handles.

    Regards,

      Thorsten


    Saturday, July 8, 2017 1:38 AM
  • Here is a code example. You will have to modify it for your exact needs.

    'draw rectangle move with mouse 
    Option Strict On
    Public Class DrawMouseMovableRectangle
        Private WithEvents PictureBox1 As New PictureBox With {.Parent = Me, .Dock = DockStyle.Fill, .BackColor = Color.Black}
        Private MouseDownStage As Integer
        Private MouseDownPt, MouseDownOffsetPt, TracerPt As PointF
        Private rect As RectangleF
    
        Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
            MouseDownStage = MouseOverRectangle(e.X, e.Y)
            Select Case MouseDownStage
                Case 1
                    MouseDownPt.X = e.X
                    MouseDownPt.Y = e.Y
                Case 2, 3
                    MouseDownPt.X = rect.X
                    MouseDownPt.Y = rect.Y
                    MouseDownOffsetPt.X = e.X - rect.X
                    MouseDownOffsetPt.Y = e.Y - rect.Y
            End Select
        End Sub
    
        Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
            If MouseDownStage > 0 Then
                Select Case MouseDownStage
                    Case 1
                        TracerPt.X = e.X
                        TracerPt.Y = e.Y
                    Case 2                    'moving fence
                        Dim dx, dy, w, h As Single
                        dx = e.X - MouseDownPt.X
                        dy = e.Y - MouseDownPt.Y
                        rect.X = MouseDownPt.X + dx - MouseDownOffsetPt.X
                        rect.Y = MouseDownPt.Y + dy - MouseDownOffsetPt.Y
                    Case 3                    'moving handle
                        rect.Width = Math.Abs(rect.X - e.X)
                        rect.Height = Math.Abs(rect.Y - e.Y)
                End Select
            Else
                Select Case MouseOverRectangle(e.X, e.Y)
                    Case 2  'fence
                        Cursor.Current = Cursors.Hand
                    Case 3 'handle
                        Cursor.Current = Cursors.SizeNESW
                    Case Else
                        Cursor.Current = Cursors.Default
                End Select
            End If
    
            PictureBox1.Invalidate()
    
        End Sub
    
        Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp
    
            Select Case MouseDownStage
                Case 1
                    rect = New RectangleF(MouseDownPt.X, MouseDownPt.Y, (TracerPt.X - MouseDownPt.X), (TracerPt.Y - MouseDownPt.Y))
            End Select
    
            MouseDownStage = 0
        End Sub
    
        Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    
            With e.Graphics
                Using p As New Pen(Color.White, 1)
                    If rect.Width > 0 Then
                        p.DashStyle = Drawing2D.DashStyle.Dash
                        .DrawRectangle(p, Rectangle.Round(rect))
    
                        p.Width = 2
                        p.Color = Color.LimeGreen
                        p.DashStyle = Drawing2D.DashStyle.Solid
    
                        Dim h As Integer = 5
                        Dim rect2 As New Rectangle(CInt(rect.X + rect.Width - h), CInt(rect.Y + rect.Height - h), 2 * h, 2 * h)
                        .DrawRectangle(p, rect2)
                    End If
    
                    If MouseDownStage = 1 Then
                        p.Color = Color.Yellow
                        p.DashStyle = Drawing2D.DashStyle.Solid
                        .DrawRectangle(p, MouseDownPt.X, MouseDownPt.Y, (TracerPt.X - MouseDownPt.X), (TracerPt.Y - MouseDownPt.Y))
                    End If
                End Using
            End With
        End Sub
    
        Private Function MouseOverRectangle(x As Integer, y As Integer) As Integer
            'determine if the mouse pointer is over the handle or fence
            Dim h As Integer = 5
            Dim handleRect As New Rectangle(CInt(rect.X + rect.Width - h), CInt(rect.Y + rect.Height - h), 2 * h, 2 * h)
            Dim fenceRect As RectangleF = rect
    
            If handleRect.Contains(x, y) Then
                MouseOverRectangle = 3              'mouse over handle
            ElseIf fenceRect.Contains(x, y) Then
                MouseOverRectangle = 2              'mouse over fence rect
            Else
                MouseOverRectangle = 1              'mouse not over anything
            End If
        End Function
    End Class

    Saturday, July 8, 2017 1:00 PM
  • Here is a code example. You will have to modify it for your exact needs.

        Private WithEvents PictureBox1 As New PictureBox With {.Parent = Me, .Dock = DockStyle.Fill, .BackColor = Color.Black}
      
        
    
          

    Well, I have a problem with this line of code

    Saturday, July 8, 2017 2:51 PM
  • Wow, is that VS2005?

    Remove the With clause and add an initialization method to setup the picturebox properties.

    Or, better yet, download VS2017 community edition and use the latest editor.  You are doing yourself a huge disservice by using such an old version of Visual Studio.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, July 8, 2017 3:02 PM
    Moderator
  • Here is a code example. You will have to modify it for your exact needs.

        Private WithEvents PictureBox1 As New PictureBox With {.Parent = Me, .Dock = DockStyle.Fill, .BackColor = Color.Black}
      
        
    
          

    Well, I have a problem with this line of code

    That line creates the picturebox with code just to make the example easy to cut and paste. You can just remove that line and add a picturebox1 to your form in the designer.

    I am not sure why the line does not work for you. Maybe as Reed says its the Visual Studio version or the .net framework version you are using.

    As I said it is an example and YOU will have to modify it for your exact needs. That way you actually learn how it works.

    :)

    Saturday, July 8, 2017 3:11 PM
  • Remove the With clause and add an initialization method to setup the picturebox properties.


    Is the initialization method to setup the picturebox properties like this ?

    Option Strict On
    Public Class Class1
        'Draw rectangle move with mouse 
    
    
        Public Class DrawMouseMovableRectangle
            Private WithEvents PictureBox1 As New PictureBox
    PictureBox1.Parent = Me
    PictureBox1 .Dock = DockStyle.Fill
     PictureBox1.BackColor = Color.Black 

    Saturday, July 8, 2017 5:09 PM
  • Remove the With clause and add an initialization method to setup the picturebox properties.


    Is the initialization method to setup the picturebox properties like this ?

    Option Strict On
    Public Class Class1
        'Draw rectangle move with mouse 
    
    
        Public Class DrawMouseMovableRectangle
            Private WithEvents PictureBox1 As New PictureBox
    PictureBox1.Parent = Me
    PictureBox1 .Dock = DockStyle.Fill
     PictureBox1.BackColor = Color.Black 

    Oman,

    Sort of.

    Just forget about that for now. If you want to get into what that is ask another question.

    Make a new project and add a picturebox to Form1 in the Visual Studio designer, then cut and paste the code example into Form1 and change the name of the example form.

    So instead of

    Public Class DrawMouseMovableRectangle Private WithEvents PictureBox1 ... 'the rest of the example goes here End Class


    You will have

        Public Class Form1
    
           'remove the line:  Private WithEvents PictureBox1 ...
           'the rest of the example goes here
    
        End Class
    

    and put a picturebox on the form in the designer and leave it named PictureBox1.

    Saturday, July 8, 2017 7:00 PM
  • Is the initialization method to setup the picturebox properties like this ?

    You are making this much more complicated than it needs to be.  You have an example of the code required to draw a rectangle:

                e.Graphics.DrawRectangle(Pens.Black, GetSelection(SelectPos, SelectSiz))

    In addition to that large rectangle you now want to draw 8 small rectangles.   So you need 8 copies of the above line, adjusted so that the rectangles are squares at the 4 corners of the large rectangle, and in the middle of each side of the large rectangle, and size of about 4.   To simplify things, get the large rectangle into a variable.   Then use the X, Y Width and Height values to calculate the four corners and the four middles.

        Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles PictureBox1.Paint
            If Selected Then
                Dim R As Rectangle = GetSelection(SelectPos, SelectSiz)
                e.Graphics.DrawRectangle(Pens.Black, R)
                e.Graphics.DrawRectangle(Pens.Black, New Rectangle(R.X - 2, R.Y - 2, 4, 4))
                e.Graphics.DrawRectangle(Pens.Black, New Rectangle(R.X - 2, R.Y + R.Height - 2, 4, 4))
                '...
                e.Graphics.DrawRectangle(Pens.Black, New Rectangle(R.X + (R.Width \ 2) - 2, R.Y - 2, 4, 4))
                e.Graphics.DrawRectangle(Pens.Black, New Rectangle(R.X - 2, R.Y + (R.Height \ 2) - 2, 4, 4))
                '...
            End If
        End Sub

    When that is working you can consider the task of making the rectangle dashed instead of solid.

    Saturday, July 8, 2017 10:59 PM