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
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) ThenRectFound = cRect Exit For End If Next Return RectFound End FunctionPrivate 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) ThenRectToChange.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 SubPrivate 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) ThenRectToChange.Width = XPos Else ' Validate the positioning... RectToChange.X = _InitialClickPosition.X RectToChange.Width = MIN_WIDTH End If End SubPrivate 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) ThenRectToChange.Height = YPos Else ' Validate the positioning... RectToChange.Height = MIN_HEIGHT RectToChange.Y = _InitialClickPosition.Y End If End SubPrivate 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) ThenRectToChange.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 IfEnd 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 IfEnd 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 SubProtected 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 ThenMe.Cursor = Cursors.Default ElseMe.Cursor = cRect.Cursor End If End IfEnd 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 EnumPrivate _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 PropertyPublic ReadOnly Property Cursor() As Cursor End PropertyPublic 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
. . .
|