We help IT Professionals succeed at work.

vb.net draw rectangle then move/resize it

What is the best method to draw a rectangle/shape  programatically then give the use the ability to select it, resize it, and move it?  Any examples??

Thanks!
Comment
Watch Question

Shahan AyyubSenior Software Engineer

Commented:
Top Expert 2015

Commented:
Does this work for you? I downloader the RuntimeResizer from this website:
http://www.codeproject.com/KB/vb/RuntimeResizer.aspx
And changed the btnCreate_Click handler as below.
Private Sub btnCreate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCreate.Click
        
        Dim cb As New System.Windows.Forms.Button()
        cb.Size = New System.Drawing.Size(200, 100)
        cb.Location = New System.Drawing.Point(50, 50)
        cb.Name = "RunTimeBtn" & CStr(cbCount)
        Me.Controls.Add(cb)
        AddHandler cb.Click, AddressOf cbDesignTime_Click

    End Sub

Open in new window

moveResizeRectangle.jpg
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Here's another approach:
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Runtime.InteropServices
Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Static R As New Random

        Dim rc As Rectangle = Me.RectangleToScreen(Me.ClientRectangle)
        Dim ptA As New Point(R.Next(rc.Left, rc.Right - 100), R.Next(rc.Top, rc.Bottom - 100))
        Dim ptB As New Point(R.Next(ptA.X + 25, rc.Right), R.Next(ptA.Y + 25, rc.Bottom))
        Dim tangleType As Elliptangle.ShapeType = IIf(R.NextDouble() <= 0.5, Elliptangle.ShapeType.Rectangle, Elliptangle.ShapeType.Ellipse)

        Dim tangle As New Elliptangle(Color.Red, tangleType)
        Me.Controls.Add(tangle)
        tangle.SetPoints(ptA, ptB)
    End Sub

End Class

Public Class Elliptangle
    Inherits UserControl

    Public Enum ShapeType
        Ellipse
        Rectangle
    End Enum

    Private Const WM_SETREDRAW As Integer = 11

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As IntPtr
    End Function

    Private Const Thickness As Integer = 4
    Private Const HandleSize As Integer = 10
    Private _ShowHandles As Boolean = False

    Private c As Color
    Private startPt As Point
    Private pbNorth As PictureBox, pbEast As PictureBox, pbSouth As PictureBox, pbWest As PictureBox
    Private st As ShapeType
    Private CMS As New ContextMenuStrip
    Private WithEvents TSI As ToolStripItem

    Public Sub New(ByVal c As Color, ByVal st As ShapeType)
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
        Me.UpdateStyles()

        Me.c = c
        Me.st = st
        Me.Cursor = Cursors.SizeAll
        Me.Size = New Size(HandleSize * 2, HandleSize * 2)
        Me.BackColor = c
        AddHandler Me.SizeChanged, AddressOf Shape_SizeChanged
        AddHandler Me.MouseDown, AddressOf Shape_MouseDown
        AddHandler Me.MouseMove, AddressOf Shape_MouseMove
        AddHandler Me.MouseEnter, AddressOf Shape_MouseEnter
        AddHandler Me.MouseLeave, AddressOf Shape_MouseLeave

        pbNorth = New PictureBox()
        pbNorth.Size = New Size(HandleSize, HandleSize)
        pbNorth.BackColor = Color.Black
        pbNorth.Cursor = Cursors.SizeNS
        AddHandler pbNorth.MouseDown, AddressOf pb_MouseDown
        AddHandler pbNorth.MouseMove, AddressOf pbNorth_MouseMove
        AddHandler pbNorth.MouseLeave, AddressOf pb_MouseLeave
        Me.Controls.Add(pbNorth)

        pbEast = New PictureBox()
        pbEast.Size = New Size(HandleSize, HandleSize)
        pbEast.BackColor = Color.Black
        pbEast.Cursor = Cursors.SizeWE
        AddHandler pbEast.MouseDown, AddressOf pb_MouseDown
        AddHandler pbEast.MouseMove, AddressOf pbEast_MouseMove
        AddHandler pbEast.MouseLeave, AddressOf pb_MouseLeave
        Me.Controls.Add(pbEast)

        pbSouth = New PictureBox()
        pbSouth.Size = New Size(HandleSize, HandleSize)
        pbSouth.BackColor = Color.Black
        pbSouth.Cursor = Cursors.SizeNS
        AddHandler pbSouth.MouseDown, AddressOf pb_MouseDown
        AddHandler pbSouth.MouseMove, AddressOf pbSouth_MouseMove
        AddHandler pbSouth.MouseLeave, AddressOf pb_MouseLeave
        Me.Controls.Add(pbSouth)

        pbWest = New PictureBox()
        pbWest.Size = New Size(HandleSize, HandleSize)
        pbWest.BackColor = Color.Black
        pbWest.Cursor = Cursors.SizeWE
        AddHandler pbWest.MouseDown, AddressOf pb_MouseDown
        AddHandler pbWest.MouseMove, AddressOf pbWest_MouseMove
        AddHandler pbWest.MouseLeave, AddressOf pb_MouseLeave
        Me.Controls.Add(pbWest)

        TSI = CMS.Items.Add("Delete")
        Me.ContextMenuStrip = CMS
        pbNorth.ContextMenuStrip = CMS
        pbEast.ContextMenuStrip = CMS
        pbSouth.ContextMenuStrip = CMS
        pbWest.ContextMenuStrip = CMS

        RecomputeRegion()
    End Sub

    Public Sub SetPoints(ByVal ptA As Point, ByVal ptB As Point) ' <-- Screen Coordinates
        If Not (Me.Parent Is Nothing) Then
            Dim rc As Rectangle = Me.NormalizedRC(ptA, ptB)
            Dim rcClient As Rectangle = Me.Parent.RectangleToClient(rc)
            Me.Location = New Point(rcClient.X - Elliptangle.HandleSize / 2, rcClient.Y - Elliptangle.HandleSize / 2)
            Me.Size = New Size(rc.Width + Elliptangle.HandleSize, rc.Height + Elliptangle.HandleSize)
        End If
    End Sub

    Private Sub pb_MouseLeave(ByVal sender As Object, ByVal e As EventArgs)
        ShowHandles = False
    End Sub

    Private Sub Shape_MouseEnter(ByVal sender As Object, ByVal e As EventArgs)
        Me.ShowHandles = True
    End Sub

    Private Sub Shape_MouseLeave(ByVal sender As Object, ByVal e As EventArgs)
        If Not pbNorth.RectangleToScreen(pbNorth.ClientRectangle).Contains(Cursor.Position) AndAlso Not pbEast.RectangleToScreen(pbEast.ClientRectangle).Contains(Cursor.Position) AndAlso Not pbSouth.RectangleToScreen(pbSouth.ClientRectangle).Contains(Cursor.Position) AndAlso Not pbWest.RectangleToScreen(pbWest.ClientRectangle).Contains(Cursor.Position) Then
            Me.ShowHandles = False
        End If
    End Sub

    Public Property ShowHandles() As Boolean
        Get
            Return _ShowHandles
        End Get
        Set(ByVal value As Boolean)
            _ShowHandles = value
            RecomputeRegion()
        End Set
    End Property

    Private Sub Shape_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            startPt = Cursor.Position
            Me.BringToFront()
        End If
    End Sub

    Private Sub Shape_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Me.Location = New Point(Me.Location.X + (curPt.X - startPt.X), Me.Location.Y + (curPt.Y - startPt.Y))
            startPt = curPt
            If Not IsNothing(Me.Parent) Then
                Me.Parent.Refresh()
            End If
        End If
    End Sub

    Private Sub Shape_SizeChanged(ByVal sender As Object, ByVal e As EventArgs)
        RecomputeRegion()
    End Sub

    Private Sub pb_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            startPt = Cursor.Position
            Me.BringToFront()
        End If
    End Sub

    Private Sub pbWest_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaX As Integer = curPt.X - startPt.X
            If Me.Width - DeltaX >= HandleSize * 2 Then
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(False), 0) ' Turn OFF updates
                Me.Width = Me.Width - DeltaX
                Me.Location = New Point(Me.Location.X + DeltaX, Me.Location.Y)
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(True), 0) ' Turn ON updates
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Private Sub pbNorth_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaY As Integer = curPt.Y - startPt.Y
            If Me.Height - DeltaY >= HandleSize * 2 Then
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(False), 0) ' Turn OFF updates
                Me.Height = Me.Height - DeltaY
                Me.Location = New Point(Me.Location.X, Me.Location.Y + DeltaY)
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(True), 0) ' Turn ON updates
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Private Sub pbSouth_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaY As Integer = curPt.Y - startPt.Y
            If Me.Height + DeltaY >= HandleSize * 2 Then
                Me.Height = Me.Height + DeltaY
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Private Sub pbEast_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaX As Integer = curPt.X - startPt.X
            If Me.Width + DeltaX >= HandleSize * 2 Then
                Me.Width = Me.Width + DeltaX
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Public Sub Draw(ByVal G As Graphics)
        Dim gp As New GraphicsPath()
        Dim RC As New Rectangle(Me.Location, Me.Size)
        RC.Inflate(-Thickness, -Thickness)
        Select Case Me.st
            Case ShapeType.Ellipse
                gp.AddEllipse(RC)
                Exit Select

            Case ShapeType.Rectangle
                gp.AddRectangle(RC)
                Exit Select
        End Select

        Using p As New Pen(Me.BackColor, Thickness)
            gp.Widen(p)
            G.DrawPath(p, gp)
        End Using
    End Sub

    Private Sub RecomputeRegion()
        Try
            Dim gp As New GraphicsPath()
            Dim RC As Rectangle = Me.ClientRectangle
            RC.Inflate(-Thickness, -Thickness)
            Select Case Me.st
                Case ShapeType.Ellipse
                    gp.AddEllipse(RC)
                    Exit Select

                Case ShapeType.Rectangle
                    gp.AddRectangle(RC)
                    Exit Select
            End Select

            Using p As New Pen(Color.Black, Thickness)
                gp.Widen(p)
            End Using

            pbNorth.Location = New Point(Me.Width / 2 - HandleSize / 2, 0)
            pbEast.Location = New Point(Me.Width - HandleSize, Me.Height / 2 - HandleSize / 2)
            pbSouth.Location = New Point(Me.Width / 2 - HandleSize / 2, Me.Height - HandleSize)
            pbWest.Location = New Point(0, Me.Height / 2 - HandleSize / 2)


            pbNorth.Visible = _ShowHandles
            pbEast.Visible = _ShowHandles
            pbSouth.Visible = _ShowHandles
            pbWest.Visible = _ShowHandles

            If _ShowHandles Then
                gp.AddRectangle(New Rectangle(pbNorth.Location, pbNorth.Size))
                gp.AddRectangle(New Rectangle(pbEast.Location, pbEast.Size))
                gp.AddRectangle(New Rectangle(pbSouth.Location, pbSouth.Size))
                gp.AddRectangle(New Rectangle(pbWest.Location, pbWest.Size))
            End If

            Me.Region = New Region(gp)
            If Not IsNothing(Me.Parent) Then
                Me.Parent.Refresh()
            End If
        Catch ex As Exception
        End Try
    End Sub

    Private Function NormalizedRC(ByVal ptA As Point, ByVal ptB As Point) As Rectangle
        Return New Rectangle(New Point(Math.Min(ptA.X, ptB.X), Math.Min(ptA.Y, ptB.Y)), New Size(Math.Abs(ptB.X - ptA.X), Math.Abs(ptB.Y - ptA.Y)))
    End Function

    Private Sub TSI_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TSI.Click
        If Not IsNothing(Me.Parent) Then
            Me.Parent.Controls.Remove(Me)
        End If
    End Sub

End Class

Open in new window

Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Forgot the screen shot...

The mouse is over the circle on the right:
(Right Click to Delete an Elliptangle)
Elliptangle.jpg
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:

Author

Commented:
I like this, Idle, (the othrs too but this seems to be closest to what I want).  Any idea why, when I try to draw on a panel that contains a video (axWindowsMediaPlayer) they go BEHIND the vdeo instead of in front?  I need them to be on top of everything (including video).
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Try using the BringToFront() method after you add it to the panel:

        Dim tangle As New Elliptangle(Color.Red, tangleType)
        Me.Controls.Add(tangle)
        tangle.SetPoints(ptA, ptB)

        tangle.BringToFront() ' <---- bring it to the front

Author

Commented:
Of course!  Perfect :-)

2 final questions...

1. Any chance of moving the handles to the corners intead of mid-sides?  Small issue, I know, but just a thought.

2. I am not sure what the underlying color might be.  That is, if its on a video that's red and I draw the rects red then they can't be seen.  How might I be able o deal with this?

Thanks again!!
Jon
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
I placed the handles in the middle so that it could easily be changed between a rectangle and a circle and still work without needing to change the code.  It could certainly be changed to the corners but would need additional work as you are then changing two sides at once instead of one.

Not sure about the color issue...no idea if you can "sample" the video.  You could add a simple feature to allow the user to change the color of the rectangles.

Author

Commented:
It would be nice if you can XOR the color (pixel) values below the rectangle edges to be inverse of the color to ensue its visible (like most select-boxes do).  I am sure it can be done.  Just running out of time :-)
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Hmm...transparency in .Net kinda sucks.

You can draw a dashed rubberband with ControlPaint.DrawReversibleFrame():
http://msdn.microsoft.com/en-us/library/system.windows.forms.controlpaint.drawreversibleframe.aspx

*** BUT *** it would not be persistent.  It would have to be constantly redrawn over the video which would make it flicker badly.  Moving the thing around with the mouse would be very difficult since the events would be trapped by the video player itself.

Since the rectangle control is based on a UserControl it can't support true transparency (again, this aspect of .Net sux).  You can "float" the  UserControl in it's own window (Form) which would allow you to make it truly transparent but then you have issues of syncrhonizing it's movement with the parent window and communication gets messy.  Plus you'd have to modify the Form itself so its Region() property gets "clipped" just as the UserControl currently does.

It might be possible but I haven't seen a clean, easy way to do it.

Author

Commented:
How about a semi-opaque fill?  Just thinking.  How would that be coded fo you think?
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
To make it semi-transparent you'd have to place the rectangles into their own forms and set the Opacity() to less than 100%.  Then they would "float" over the main form.  A LOT more code would have to be written to restrict them to the area of the video on the form below.  They would need to minimize/hide when the main form is minimized or obscured.  When the main form is moved they would have to shifted as well.  The floating forms would have to be clipped using the Region() property in the same manner as the UserControl is now.  Plus you have to now communicate between all these multiple forms...  =\

Overlapping controls in .Net just don't work the way most people think they should.  =(

Author

Commented:
I think I have the code to make a control transparent (without needing a form).  However its acting odd.  Its making the edges transparent (opaque) but doing nothing to the center.  I am doing a FillRectangle on your control to do it.

In addition, is there any way to get the handles to show when the mouse is inside the box instead of just on the edge?  I am using a line size of 1px and its kinda hard to work with.

Thanks again!
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
The control does NOT exist in the middle...this is why you see the video thru it.  =)

It is being clipped (not drawn at all) because the Region() of the UserControl has been set to be only that where the rectangle lines (and handles) are.  This occurs in the RecomputeRegion() method.

The control is not really transparent but simply has a HOLE in the middle....

Author

Commented:
I am taking this appart bit by bit and adjusting it as it needed.  I am adding a OnClick handler so that I can select a rectangle to be worked on (outside the object).  I would like the color to change when _selected.  Whatever I try I cannot seem to get the color to change.  I figured it would be in the RecomputeRegion() area but I'm missing something.

Also I need a callback/event that fires when _selected changes.  (boolean)
Can you help here by chance?

Thanks!
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Can you show me the changes you made?

Author

Commented:
Not much different.  Just tidying things up a bit.  Got the event working.  Just need to get the color to change.
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Runtime.InteropServices

Public Class Elliptangle
    Inherits UserControl

    Public Enum ShapeType
        Ellipse
        Rectangle
    End Enum

    Private Enum PBWall
        North
        South
        East
        West
    End Enum

    Private Const WM_SETREDRAW As Integer = 11

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As IntPtr
    End Function

    Public Thickness As Integer = 4
    Public HandleSize As Integer = 10
    Private _ShowHandles As Boolean = False
    Private _Selected As Boolean = False

    Private BoxColor As Color
    Private startPt As Point
    Private pbNorth As PictureBox, pbEast As PictureBox, pbSouth As PictureBox, pbWest As PictureBox
    Private st As ShapeType
    Private CMS As New ContextMenuStrip
    Private WithEvents TSI As ToolStripItem
    Public Event OnSelect(ByRef Sender As Elliptangle)
    Public ReadOnly Property Selected() As Boolean
        Get
            Return _Selected
        End Get
    End Property

    Private Function InitPB(ByRef Wall As PBWall) As PictureBox
        Dim Temp As PictureBox
        Temp = New PictureBox
        Temp.Size = New Size(HandleSize, HandleSize)
        Temp.BackColor = Color.Black
        Select Case Wall
            Case PBWall.North
                Temp.Cursor = Cursors.SizeNS
                AddHandler Temp.MouseMove, AddressOf pbNorth_MouseMove
            Case PBWall.South
                Temp.Cursor = Cursors.SizeNS
                AddHandler Temp.MouseMove, AddressOf pbSouth_MouseMove
            Case PBWall.East
                Temp.Cursor = Cursors.SizeWE
                AddHandler Temp.MouseMove, AddressOf pbEast_MouseMove
            Case PBWall.West
                Temp.Cursor = Cursors.SizeWE
                AddHandler Temp.MouseMove, AddressOf pbWest_MouseMove

        End Select
        AddHandler Temp.MouseClick, AddressOf pb_MouseClick
        AddHandler Temp.MouseDown, AddressOf pb_MouseDown
        AddHandler Temp.MouseLeave, AddressOf pb_MouseLeave
        Return Temp

    End Function

    Public Sub New(ByVal BoxColor As Color, ByVal st As ShapeType)
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
        Me.UpdateStyles()

        Me.BoxColor = BoxColor
        Me.st = st
        Me.Cursor = Cursors.SizeAll
        Me.Size = New Size(HandleSize * 2, HandleSize * 2)
        Me.BackColor = BoxColor
        AddHandler Me.SizeChanged, AddressOf Shape_SizeChanged
        AddHandler Me.MouseClick, AddressOf Shape_MouseClick
        AddHandler Me.MouseDown, AddressOf Shape_MouseDown
        AddHandler Me.MouseMove, AddressOf Shape_MouseMove
        AddHandler Me.MouseEnter, AddressOf Shape_MouseEnter
        AddHandler Me.MouseLeave, AddressOf Shape_MouseLeave

        pbNorth = InitPB(PBWall.North)
        pbEast = InitPB(PBWall.East)
        pbSouth = InitPB(PBWall.South)
        pbWest = InitPB(PBWall.West)

        Me.Controls.Add(pbNorth)
        Me.Controls.Add(pbEast)
        Me.Controls.Add(pbSouth)
        Me.Controls.Add(pbWest)


        TSI = CMS.Items.Add("Delete")
        Me.ContextMenuStrip = CMS
        pbNorth.ContextMenuStrip = CMS
        pbEast.ContextMenuStrip = CMS
        pbSouth.ContextMenuStrip = CMS
        pbWest.ContextMenuStrip = CMS

        RecomputeRegion()
    End Sub

    Public Sub SetPoints(ByVal ptA As Point, ByVal ptB As Point) ' <-- Screen Coordinates
        If Not (Me.Parent Is Nothing) Then
            Dim rc As Rectangle = Me.NormalizedRC(ptA, ptB)
            Dim rcClient As Rectangle = Me.Parent.RectangleToClient(rc)
            Me.Location = New Point(rcClient.X - HandleSize / 2, rcClient.Y - HandleSize / 2)
            Me.Size = New Size(rc.Width + HandleSize, rc.Height + HandleSize)
        End If
    End Sub

    Private Sub pb_MouseClick(ByVal sender As Object, ByVal e As EventArgs)
        _Selected = Not _Selected
        If _Selected Then RaiseEvent OnSelect(Me)
        RecomputeRegion()
    End Sub
    Private Sub Shape_MouseClick(ByVal sender As Object, ByVal e As EventArgs)
        _Selected = Not _Selected
        If _Selected Then RaiseEvent OnSelect(Me)
        RecomputeRegion()
    End Sub
    Private Sub pb_MouseLeave(ByVal sender As Object, ByVal e As EventArgs)
        ShowHandles = False
    End Sub

    Private Sub Shape_MouseEnter(ByVal sender As Object, ByVal e As EventArgs)
        Me.ShowHandles = True
    End Sub

    Private Sub Shape_MouseLeave(ByVal sender As Object, ByVal e As EventArgs)
        If Not pbNorth.RectangleToScreen(pbNorth.ClientRectangle).Contains(Cursor.Position) AndAlso Not pbEast.RectangleToScreen(pbEast.ClientRectangle).Contains(Cursor.Position) AndAlso Not pbSouth.RectangleToScreen(pbSouth.ClientRectangle).Contains(Cursor.Position) AndAlso Not pbWest.RectangleToScreen(pbWest.ClientRectangle).Contains(Cursor.Position) Then
            Me.ShowHandles = False
        End If
    End Sub

    Public Property ShowHandles() As Boolean
        Get
            Return _ShowHandles
        End Get
        Set(ByVal value As Boolean)
            _ShowHandles = value
            RecomputeRegion()
        End Set
    End Property

    Private Sub Shape_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            startPt = Cursor.Position
            Me.BringToFront()
        End If
    End Sub

    Private Sub Shape_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Me.Location = New Point(Me.Location.X + (curPt.X - startPt.X), Me.Location.Y + (curPt.Y - startPt.Y))
            startPt = curPt
            If Not IsNothing(Me.Parent) Then
                Me.Parent.Refresh()
            End If
        End If
    End Sub

    Private Sub Shape_SizeChanged(ByVal sender As Object, ByVal e As EventArgs)
        RecomputeRegion()
    End Sub

    Private Sub pb_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            startPt = Cursor.Position
            Me.BringToFront()
        End If
    End Sub

    Private Sub pbWest_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaX As Integer = curPt.X - startPt.X
            If Me.Width - DeltaX >= HandleSize * 2 Then
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(False), 0) ' Turn OFF updates
                Me.Width = Me.Width - DeltaX
                Me.Location = New Point(Me.Location.X + DeltaX, Me.Location.Y)
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(True), 0) ' Turn ON updates
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Private Sub pbNorth_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaY As Integer = curPt.Y - startPt.Y
            If Me.Height - DeltaY >= HandleSize * 2 Then
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(False), 0) ' Turn OFF updates
                Me.Height = Me.Height - DeltaY
                Me.Location = New Point(Me.Location.X, Me.Location.Y + DeltaY)
                SendMessage(Me.Handle, WM_SETREDRAW, Convert.ToInt32(True), 0) ' Turn ON updates
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Private Sub pbSouth_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaY As Integer = curPt.Y - startPt.Y
            If Me.Height + DeltaY >= HandleSize * 2 Then
                Me.Height = Me.Height + DeltaY
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Private Sub pbEast_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            Dim curPt As Point = Cursor.Position
            Dim DeltaX As Integer = curPt.X - startPt.X
            If Me.Width + DeltaX >= HandleSize * 2 Then
                Me.Width = Me.Width + DeltaX
                RecomputeRegion()
                startPt = curPt
            End If
        End If
    End Sub

    Public Sub Draw(ByVal G As Graphics)
        Dim gp As New GraphicsPath()
        Dim RC As New Rectangle(Me.Location, Me.Size)
        RC.Inflate(-Thickness, -Thickness)
        Select Case Me.st
            Case ShapeType.Ellipse
                gp.AddEllipse(RC)
                Exit Select

            Case ShapeType.Rectangle
                gp.AddRectangle(RC)
                Exit Select
        End Select

        Using p As New Pen(Me.BackColor, Thickness)
            gp.Widen(p)
            G.DrawPath(p, gp)
        End Using
    End Sub

    Private Sub RecomputeRegion()
        Try

            Dim gp As New GraphicsPath()
            Dim RC As Rectangle = Me.ClientRectangle
            RC.Inflate(-Thickness, -Thickness)
            Select Case Me.st
                Case ShapeType.Ellipse                    
                    gp.AddEllipse(RC)
                    Exit Select

                Case ShapeType.Rectangle
                    gp.AddRectangle(RC)
                    Exit Select
            End Select

            Using p As New Pen(Color.Black, Thickness)
                gp.Widen(p)
            End Using

            pbNorth.Location = New Point(Me.Width / 2 - HandleSize / 2, 0)
            pbEast.Location = New Point(Me.Width - HandleSize, Me.Height / 2 - HandleSize / 2)
            pbSouth.Location = New Point(Me.Width / 2 - HandleSize / 2, Me.Height - HandleSize)
            pbWest.Location = New Point(0, Me.Height / 2 - HandleSize / 2)


            pbNorth.Visible = _ShowHandles
            pbEast.Visible = _ShowHandles
            pbSouth.Visible = _ShowHandles
            pbWest.Visible = _ShowHandles

            If _ShowHandles Then
                gp.AddRectangle(New Rectangle(pbNorth.Location, pbNorth.Size))
                gp.AddRectangle(New Rectangle(pbEast.Location, pbEast.Size))
                gp.AddRectangle(New Rectangle(pbSouth.Location, pbSouth.Size))
                gp.AddRectangle(New Rectangle(pbWest.Location, pbWest.Size))
            End If

            Me.Region = New Region(gp)
            If Not IsNothing(Me.Parent) Then
                Me.Parent.Refresh()
            End If
        Catch ex As Exception
        End Try
    End Sub

    Private Function NormalizedRC(ByVal ptA As Point, ByVal ptB As Point) As Rectangle
        Return New Rectangle(New Point(Math.Min(ptA.X, ptB.X), Math.Min(ptA.Y, ptB.Y)), New Size(Math.Abs(ptB.X - ptA.X), Math.Abs(ptB.Y - ptA.Y)))
    End Function

    Private Sub TSI_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TSI.Click
        If Not IsNothing(Me.Parent) Then
            Me.Parent.Controls.Remove(Me)
        End If
    End Sub

End Class

Open in new window

Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
To change the color from within you just set the BackColor:

    Me.BackColor = Color.SomeOtherColor

Same thing from outside the control except you replace "Me" with an instance variable:

    SomeInstanceHere.BackColor = Color.SomeOtherColor

If you give your Selected() property a "setter" (instead of read only) then you can raise the event, change the backcolor, and call RecomputeRegion() all from the same place without having to duplicate that code.
(Do it from within  the property!)

Author

Commented:
Hmm, i tried that exactly but it didn't seem to work.  Me.BackColor.  I'll try some more.

Author

Commented:
You're really helping a lot.  I have another issue though and I'm nearly done.  About every other time I run the program (without chaning a line of code) when the boxes are drawn and then dragged, they leave a trail behind them.  Like the underlying area is not being redrawn at all  The box just moves leaving boxes behind.  Any idea how to clear this up?
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
The Form below is not redrawing itself for some reason.

You could explicitly force the parent container to refresh when the LocationChanged() event fires:

    Private Sub UserControl1_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
        If Not IsNothing(Me.Parent) Then
            Me.Parent.Refresh()
        End If
    End Sub

*This is already being done in RecomputeRegion() to force the parent to refresh when the size is changed.  Be aware that this can degrade performance of the form...depends what is on it and how many controls are involved.

Author

Commented:
Hmm, how do I implement this?  Just drop the code in the class?  I also meant to tell you that now and again, when I click to drag a rectangle, it vanishes completely.  About once every 20 times or so.
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Yes...just add that to the class.  No idea why one would vanish...would probably need to play with your actual project (or a pared down one) to figure it out.

Author

Commented:
I did add it to the code (as I figured) and it still doesn't refresh always.  Very odd.

Author

Commented:
More info:

1. The controls are being added to a panel (not the form) so refresh (parent.refresh) is occuring on the panel (which is good)

2. The panel has a MS Video player embedded on it (Com object)

3. The problems arrise, it seems, only when the player is visible.  If  hide the player, I don't get the trails, ever, thus far.  

I cannot seem to figure out any way to get it to be perfect.  I even tried a slight delay between loading the player and adding the rectangles, to no avail.
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Are the trails appearing only on the Panel?...only over the Video Player?...or on both?

Does the player take up the whole panel?

The player is probably the one not refreshing itself causing the trails....

Author

Commented:
the video player is maxed to the size of the panel.  So the rectangles are always on the video.

Author

Commented:
I exposed the OnLocationChanged event and added Player.Refresh() to when the location changes...still has issues.
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Calling Refresh() against the video player doesn't necessarily mean it will cause the video portion to update...it may just update the controls/buttons portion (if they are visible).  Many video players use DirectX type approaches to render the video directly to the screen so it wouldn't know if a portion of itself has been obscured (when a rectangle gets moved).  It may just be updating the portions of the video that have changed from the last frame.

Author

Commented:
YEa I figured.  Just don't know what set of variables to trigger to get this to function correctly all the time.  About 20% of the time I have to restart the application to get them to go right.
High School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009
Commented:
Going back to the "floating" concept...if you created a borderless form and housed all of the usercontrol rectangles on that then you could set the Opacity() of the transparent form so that you could see the video thru it.  This way the floating form would refresh itself when the rectangles get moved and the video can do its thing underneath.  The complexity goes was up though...

I don't know of any easier solutions for dealing with the video player problem.  If you wrote the video player yourself from the ground up this would another story as you could integrate the rectangles into the rendering loop (this is way beyond my ability though!).

Author

Commented:
Well this is all sorta a proof-of-concept right now.  I just want to get "something to work".  I'll probably come back to the floating form method once I get past this.

Author

Commented:
Still working on this but yours is the basis of my solution