Drydo's Blog

Teenager of the Internet

This blog hosted by:
http://blogs.vbcity.com
  Home :: Syndication  :: Login   Community Forums   :: vbCity.com   :: DevCity.NET  

Thought I'd punch this little fella up for your perusal.  Basically, it’s a base control that's resizable at runtime allowing the user to resize the control to whatever size (although I have implemented a minimum size check - check out the comments).  I've seen a couple of people / organisations that are selling similar items and rather than take the path of least resistance ;-) - I'd thought it would be fun do one myself.

Anyhoo, most of its basic stuff, however, the most interesting part of the development I found was changing more than one dimension caused the control is flicker and creep around.  In fact, the faster you moved the mouse the further the control was liable to creep.  Checking out the documentation, whilst you can prevent control's and parent control's layout from being called - changing say the .Left or .Height still caused resize event to be called and not to the control itself but its parent.  So if you wanted to expand the control from its left hand border, you would need to adjust the control's .Width and .Left values - but interestingly when you changed the .Left position, the control perform and update this before changing the .Width with its communication back up to the parent.

The way to get around this was to use the 'SetBounds' method to changed the position and size of the control, e.g. perform the resize in one hit rather than one or more go's.  Now I'll admit this may not be the best approach - but works reasonably well and is a potentially good starting point...

Have fun - M

Code CopyHideScrollFull
Public Class BaseAuthoringControl

#Region "Constants"
' Resizing Constants
Private
Const HIT_WIDTH As Integer = 5
Private
Const MIN_WIDTH As Integer = 100
Private
Const MIN_HEIGHT As Integer = 50

#End Region

#Region "Private"
' Resize elements
Private
_ResizeRects As New List(Of ResizeRect)
Private
_CurrentRectHit As ResizeRect
Private
_InitialClickPosition As Rectangle = Nothing
Private
_MouseDown As Boolean = False
Private
_PreventPaint As Boolean

#End Region
Public Sub New()
' Base initialiser
MyBase
.New()
' Set the base minimum sizes

Me
.MinimumSize = New Size(MIN_WIDTH, MIN_HEIGHT)
' This call is required by the Windows Form Designer.

InitializeComponent()
' Generate the initial Resize Rects

ResetResizeAreas()
' Set owner drawn parameters
Me
.SetStyle( _
   ControlStyles.OptimizedDoubleBuffer Or _
   ControlStyles.UserPaint Or _
   ControlStyles.AllPaintingInWmPaint, _
   True)
' Prevent redraws on control resize

Me
.SetStyle( _
ControlStyles.ResizeRedraw, _
False)
End Sub

#Region "Resize Functions"
Private Sub ResetResizeAreas()
' This routine ensures that the all resize hit areas are resized to the current position on the control
Me
._ResizeRects.Clear()
' Top Left
_ResizeRects.Add(New ResizeRect(New Rectangle(0, 0, HIT_WIDTH, HIT_WIDTH), ResizeRect.RectangleType.TopLeft))
' Top

_ResizeRects.Add(New ResizeRect(New Rectangle(HIT_WIDTH, 0, Me.Size.Width - (HIT_WIDTH * 2), HIT_WIDTH), ResizeRect.RectangleType.Top))
' Top Right

_ResizeRects.Add(New ResizeRect(New Rectangle(Me.Size.Width - HIT_WIDTH, 0, HIT_WIDTH, HIT_WIDTH), ResizeRect.RectangleType.TopRight))
' Left
_ResizeRects.Add(New ResizeRect(New Rectangle(0, 0 + HIT_WIDTH, HIT_WIDTH, Me.Size.Height - (HIT_WIDTH * 2)), ResizeRect.RectangleType.Left))
' Right

_ResizeRects.Add(New ResizeRect(New Rectangle(Me.Size.Width - HIT_WIDTH, 0 + HIT_WIDTH, HIT_WIDTH, Me.Size.Height - (HIT_WIDTH * 2)), ResizeRect.RectangleType.Right))
' Bottom Left
_ResizeRects.Add(New ResizeRect(New Rectangle(0, Me.Size.Height - HIT_WIDTH, HIT_WIDTH, HIT_WIDTH), ResizeRect.RectangleType.BottomLeft))
' Bottom

_ResizeRects.Add(New ResizeRect(New Rectangle(HIT_WIDTH, Me.Size.Height - HIT_WIDTH, Me.Size.Width - (HIT_WIDTH * 2), HIT_WIDTH), ResizeRect.RectangleType.Bottom))
' Bottom Right

_ResizeRects.Add(New ResizeRect(New Rectangle(Me.Size.Width - HIT_WIDTH, Me.Size.Height - HIT_WIDTH, HIT_WIDTH, HIT_WIDTH), ResizeRect.RectangleType.BottomRight))
End Sub
Private Function OnEdge(ByVal MousePosition As Point) As ResizeRect
' This routine loops through the arraylist of rects and determine whether the
' point passed through is within one of the resize rects

Dim
RectFound As ResizeRect = Nothing
For
Each cRect As ResizeRect In Me._ResizeRects
If cRect.HitArea.Contains(MousePosition) Then
RectFound = cRect
Exit
For
End If
Next
Return
RectFound
End Function
Private Sub MoveLeft(ByVal XPos As Integer, ByRef RectToChange As Rectangle)
' Moves the control to the left and adjusts the width to compensate
If
Me.Width > MIN_WIDTH OrElse (Me.Width <= MIN_WIDTH AndAlso (XPos) < 0) Then
RectToChange.X = Me.Left + XPos
RectToChange.Width = Me.Width + -XPos
Else
' Validate the positioning...
RectToChange.X = Me._InitialClickPosition.X + Me._InitialClickPosition.Width - MIN_WIDTH
RectToChange.Width = MIN_WIDTH
End If
End Sub
Private Sub MoveRight(ByVal XPos As Integer, ByRef RectToChange As Rectangle)
' Moves the control to the right
If
Me.Width > MIN_WIDTH OrElse (Me.Width <= MIN_WIDTH AndAlso (XPos - MIN_WIDTH) >= 0) Then
RectToChange.Width = XPos
Else
' Validate the positioning...
RectToChange.X = _InitialClickPosition.X
RectToChange.Width = MIN_WIDTH
End If
End Sub
Private Sub MoveDown(ByVal YPos As Integer, ByRef RectToChange As Rectangle)
' Moves the control down
If
Me.Height > MIN_HEIGHT OrElse (Me.Height <= MIN_HEIGHT AndAlso (YPos - MIN_HEIGHT) >= 0) Then
RectToChange.Height = YPos
Else
' Validate the positioning...
RectToChange.Height = MIN_HEIGHT
RectToChange.Y = _InitialClickPosition.Y
End If
End Sub
Private Sub MoveUp(ByVal YPos As Integer, ByRef RectToChange As Rectangle)
' Moves the control up and compensates with the height of the control
If
Me.Height > MIN_HEIGHT OrElse (Me.Height <= MIN_HEIGHT AndAlso (YPos) < 0) Then
RectToChange.Height = Me.Height + -YPos
RectToChange.Y = Me.Top + YPos
Else
' Validate the positioning...
RectToChange.Height = MIN_HEIGHT
RectToChange.Y = Me._InitialClickPosition.Y + Me._InitialClickPosition.Height - MIN_HEIGHT
End If
End Sub

#End Region

#Region "Mouse Event Capture"
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
' Prevent any top level layout handling
Me
.SuspendLayout()
Me
.Parent.SuspendLayout()
' Set the initial click position

_InitialClickPosition = New Rectangle(Me.Location, Me.Size)
' Are we currently on the control's edge?

Dim
FoundRect As ResizeRect = OnEdge(New Point(e.X, e.Y))
If
Not FoundRect Is Nothing Then
' If so, record
_CurrentRectHit = FoundRect
_MouseDown = True
End If
End Sub
Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
' Resume top level and current layout
Me
.Parent.ResumeLayout(False)
Me
.ResumeLayout()
' Reset appropriate elements

_MouseDown = False
Me
.ResetResizeAreas()
Me
._CurrentRectHit = Nothing
End Sub
Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
' Should we show the cursor?
If
_MouseDown Then
_PreventPaint = True
' Show the appropriate cursor
Me
.Cursor = Me._CurrentRectHit.Cursor
Dim
NewBounds As New Rectangle(Me.Left, Me.Top, Me.Width, Me.Height)
' Determine the resizing capabilities
Select
Case _CurrentRectHit.RectType
Case ResizeRect.RectangleType.Top
MoveUp(e.Y, NewBounds)
Case ResizeRect.RectangleType.TopLeft
MoveLeft(e.X, NewBounds)
MoveUp(e.Y, NewBounds)
Case ResizeRect.RectangleType.TopRight
MoveRight(e.X, NewBounds)
MoveUp(e.Y, NewBounds)
Case ResizeRect.RectangleType.Left
MoveLeft(e.X, NewBounds)
Case ResizeRect.RectangleType.Right
MoveRight(e.X, NewBounds)
Case ResizeRect.RectangleType.Bottom
MoveDown(e.Y, NewBounds)
Case ResizeRect.RectangleType.BottomRight
MoveRight(e.X, NewBounds)
MoveDown(e.Y, NewBounds)
Case ResizeRect.RectangleType.BottomLeft
MoveLeft(e.X, NewBounds)
MoveDown(e.Y, NewBounds)
End Select
' Set the new bounds of the control
Me
.SetBounds(NewBounds.X, NewBounds.Y, NewBounds.Width, NewBounds.Height)
' When completed, resize the hit rects
Me
.ResetResizeAreas()
_PreventPaint = False
Me
.Invalidate()
Else
' Are we over a resize rect?
Dim
cRect As ResizeRect = Me.OnEdge(New Point(e.X, e.Y))
If
cRect Is Nothing Then
Me.Cursor = Cursors.Default
Else
Me.Cursor = cRect.Cursor
End If
End If
End Sub

#End Region

#Region "Painting"
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
' Call base method
MyBase
.OnPaint(e)
' Used for debugging (display the resize rectangles)
'For Each cRect As ResizeRect In Me._ResizeRects

'e.Graphics.FillRectangle(New SolidBrush(Color.FromArgb(CInt(Int((255 * Rnd()) + 1)), CInt(Int((255 * Rnd()) + 1)), CInt(Int((255 * Rnd()) + 1)))), cRect.HitArea)

'Next
End Sub
Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
' Call base method
MyBase
.OnPaintBackground(e)
' Paint a custom border

e.Graphics.DrawRectangle(Pens.Blue, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1))
End Sub

#End Region

End
Class

Public
Class ResizeRect
Public Enum RectangleType
Top
TopLeft
TopRight
Left
Right
BottomLeft
Bottom
BottomRight
End Enum
Private _HitArea As Rectangle
Private
_RectType As RectangleType
Private
_Cursor As Cursor
Public Sub New(ByVal HitArea As Rectangle, ByVal RectType As RectangleType)
' Set properities
Me
.HitArea = HitArea
Me
.RectType = RectType
End Sub

#Region "Properities"
Public Property HitArea() As Rectangle
Get
Return _HitArea
End Get
Set
(ByVal value As Rectangle)
_HitArea = value
End Set
End Property
Public ReadOnly Property Cursor() As Cursor
Get
Return _Cursor
End Get
End Property
Public Property RectType() As RectangleType
Get
Return _RectType
End Get
Set
(ByVal Value As RectangleType)
' Set the value
_RectType = Value
' Then set the appropriate cursor

Select
Case Value
Case RectangleType.Top, RectangleType.Bottom
_Cursor = Cursors.SizeNS
Case RectangleType.TopLeft, RectangleType.BottomRight
_Cursor = Cursors.SizeNWSE
Case RectangleType.TopRight, RectangleType.BottomLeft
_Cursor = Cursors.SizeNESW
Case RectangleType.Left, RectangleType.Right
_Cursor = Cursors.SizeWE
End Select
End Set
End Property

#End Region

End
Class

. . .
posted on Tuesday, May 30, 2006 12:11 PM