Move and Resize a Control or a Borderless Form - using window messages (smooth!)-VBForums
Results 1 to 33 of 33

Thread: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Hi,

    Here is a small example of how to move and resize a form without a border (FormBorderStyle = None).

    Instead of manually setting the Location and Size of the Form, I have decided to use the SendMessage function to handle it. This results in a much smoother experience, (especially with lots of controls) and will also make sure that the form always follows the mouse, regardless of how fast you move it. In other examples you will often notice that if you move your mouse too fast, the form can no longer keep up and will stop moving.


    The example uses a Panel as the form's caption. Dragging the panel will move the form, just like the real caption of a form with a border would behave.
    You can of course decide to use a different control. You could even use the form itself. I just thought a Panel was the easiest example.

    For resizing, I did not use any special controls; I merely use a BorderWith constant (default: 6) that defines the width of the area you can use to resize the form.

    Screenshot:

    (You can of course use nice icons for the buttons instead, but that was not the purpose of this codebank submission)



    The form moving works pretty simple: when the MouseDown event of the Panel is fired, a simple check is done to make sure that the user clicked the Left button instead of the Right button, and that the form is not maximized (you cannot move a maximized form).
    Then, a MoveForm() method is called, in which the SendMessage function is called with the correct parameters.
    vb.net Code:
    1. Private Sub pnlCaption_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pnlCaption.MouseDown
    2.         If e.Button = Windows.Forms.MouseButtons.Left And Me.WindowState <> FormWindowState.Maximized Then
    3.             MoveForm()
    4.         End If
    5.     End Sub
    6.  
    7.     Private Sub MoveForm()
    8.         ReleaseCapture()
    9.         SendMessage(Me.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0)
    10.     End Sub


    The resizing is a little more involved, as it requires a check for the position of the mouse cursor, and changes the cursor to a resizing cursor.
    For this, I made a Property resizeDir which changes the cursor depending on which value it gets automatically.
    The value of this property is used in a ResizeForm method, which calls the SendMessage function again with the correct parameters (depending on the position of the mouse, or the resizeDir value).
    vb.net Code:
    1. Private Sub Form1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
    2.         If e.Button = Windows.Forms.MouseButtons.Left And Me.WindowState <> FormWindowState.Maximized Then
    3.             ResizeForm(resizeDir)
    4.         End If
    5.     End Sub
    6.  
    7.     Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
    8.         'Calculate which direction to resize based on mouse position
    9.  
    10.         If e.Location.X < BorderWidth And e.Location.Y < BorderWidth Then
    11.             resizeDir = ResizeDirection.TopLeft
    12.  
    13.         ElseIf e.Location.X < BorderWidth And e.Location.Y > Me.Height - BorderWidth Then
    14.             resizeDir = ResizeDirection.BottomLeft
    15.  
    16.         ElseIf e.Location.X > Me.Width - BorderWidth And e.Location.Y > Me.Height - BorderWidth Then
    17.             resizeDir = ResizeDirection.BottomRight
    18.  
    19.         ElseIf e.Location.X > Me.Width - BorderWidth And e.Location.Y < BorderWidth Then
    20.             resizeDir = ResizeDirection.TopRight
    21.  
    22.         ElseIf e.Location.X < BorderWidth Then
    23.             resizeDir = ResizeDirection.Left
    24.  
    25.         ElseIf e.Location.X > Me.Width - BorderWidth Then
    26.             resizeDir = ResizeDirection.Right
    27.  
    28.         ElseIf e.Location.Y < BorderWidth Then
    29.             resizeDir = ResizeDirection.Top
    30.  
    31.         ElseIf e.Location.Y > Me.Height - BorderWidth Then
    32.             resizeDir = ResizeDirection.Bottom
    33.  
    34.         Else
    35.             resizeDir = ResizeDirection.None
    36.         End If
    37.     End Sub
    38.  
    39. Private Sub ResizeForm(ByVal direction As ResizeDirection)
    40.         Dim dir As Integer = -1
    41.         Select Case direction
    42.             Case ResizeDirection.Left
    43.                 dir = HTLEFT
    44.             Case ResizeDirection.TopLeft
    45.                 dir = HTTOPLEFT
    46.             Case ResizeDirection.Top
    47.                 dir = HTTOP
    48.             Case ResizeDirection.TopRight
    49.                 dir = HTTOPRIGHT
    50.             Case ResizeDirection.Right
    51.                 dir = HTRIGHT
    52.             Case ResizeDirection.BottomRight
    53.                 dir = HTBOTTOMRIGHT
    54.             Case ResizeDirection.Bottom
    55.                 dir = HTBOTTOM
    56.             Case ResizeDirection.BottomLeft
    57.                 dir = HTBOTTOMLEFT
    58.         End Select
    59.  
    60.         If dir <> -1 Then
    61.             ReleaseCapture()
    62.             SendMessage(Me.Handle, WM_NCLBUTTONDOWN, dir, 0)
    63.         End If
    64.     End Sub


    I attached the full example (without any compiled files, so you will probably need to Build it first) so you can play around with it.

    Enjoy!
    Attached Files Attached Files

  2. #2

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a borderless form - using window messages (smooth!)

    To move or resize any Control, you can use the attached class. It is basically the same code as the move/resize Form code, but now wrapped into a class.

    Example usage is easy.
    Simply create a new instance of the MoveControl class and pass the control you want to be able to resize. You can make multiple controls like this simply by creating multiple instances of the MoveControl class, with a different control each time.

    vb.net Code:
    1. Dim moveCtrl1 As New MoveControl(TextBox1)
    2. Dim moveCtrl2 As New MoveControl(ProgressBar1)


    NEW: You can now set some options such as AllowMoving, to enable or disable moving. You can also set the AllowResizingFlags property, to which you can pass a bitwise combination of flags (bottom, left, top, bottomleft, bottomright, etc) that the control can resize in.

    Example usage:
    vb.net Code:
    1. 'Create new MoveControl class to allow moving/resizing
    2.         Dim moveCtrl As New MoveControl(ProgressBar1)
    3.  
    4.         'Disable moving
    5.         moveCtrl.AllowMoving = False
    6.  
    7.         'Set resizing to horizontal and vertical only, no 'sideways' resizing:
    8.         moveCtrl.AllowResizeFlags = MoveControl.ResizeDirection.Left Or _
    9.                                     MoveControl.ResizeDirection.Right Or _
    10.                                     MoveControl.ResizeDirection.Top Or _
    11.                                     MoveControl.ResizeDirection.Bottom
    12.  
    13.         'On second thought, let's remove the Left resizing option:
    14.         moveCtrl.AllowResizeFlags = moveCtrl.AllowResizeFlags And Not MoveControl.ResizeDirection.Left

    As you can see, you use the "Or" operator to enable multiple directions as in the example (by default, all directions are enabled).
    To remove a single direction from the allowed directions, you can use the "And Not" operator as in the example.
    Use "ResizeDirection.None" to disable resizing completely.

    Any disabled resizing directions will also stop showing the resizing icon.


    Also attached is a small example project.



    Code in the class:
    vb.net Code:
    1. Imports System.Runtime.InteropServices
    2.  
    3. Public Class MoveControl
    4.  
    5.     Private _ctrl As Control
    6.     Public Sub New(ByVal ctrl As Control)
    7.         _ctrl = ctrl
    8.  
    9.         If _ctrl IsNot Nothing Then
    10.             AddHandler _ctrl.MouseDown, AddressOf MouseDown
    11.             AddHandler _ctrl.MouseMove, AddressOf MouseMove
    12.         Else
    13.             Throw New ArgumentException("Object 'ctrl' not set to any control.")
    14.         End If
    15.     End Sub
    16.  
    17. #Region " Options "
    18.  
    19.     Private _AllowMoving As Boolean = True
    20.     Public Property AllowMoving() As Boolean
    21.         Get
    22.             Return _AllowMoving
    23.         End Get
    24.         Set(ByVal value As Boolean)
    25.             _AllowMoving = value
    26.         End Set
    27.     End Property
    28.  
    29.     Private _AllowResizeFlags As ResizeDirection = ResizeDirection.Bottom Or _
    30.                                                     ResizeDirection.BottomLeft Or _
    31.                                                     ResizeDirection.BottomRight Or _
    32.                                                     ResizeDirection.Left Or _
    33.                                                     ResizeDirection.Right Or _
    34.                                                     ResizeDirection.Top Or _
    35.                                                     ResizeDirection.TopLeft Or _
    36.                                                     ResizeDirection.TopRight
    37.  
    38.     Public Property AllowResizeFlags() As ResizeDirection
    39.         Get
    40.             Return _AllowResizeFlags
    41.         End Get
    42.         Set(ByVal value As ResizeDirection)
    43.             _AllowResizeFlags = value
    44.         End Set
    45.     End Property
    46.  
    47. #End Region
    48.  
    49.     'Width of the 'resizable border', the area where you can resize.
    50.     Private Const BorderWidth As Integer = 6
    51.     Private _resizeDir As ResizeDirection = ResizeDirection.None
    52.  
    53.     <Flags()> _
    54.     Public Enum ResizeDirection
    55.         None = 0
    56.         Left = 1
    57.         TopLeft = 2
    58.         Top = 4
    59.         TopRight = 8
    60.         Right = 16
    61.         BottomRight = 32
    62.         Bottom = 64
    63.         BottomLeft = 128
    64.     End Enum
    65.  
    66.     Private Property resizeDir() As ResizeDirection
    67.         Get
    68.             Return _resizeDir
    69.         End Get
    70.         Set(ByVal value As ResizeDirection)
    71.             _resizeDir = value
    72.  
    73.             'Change cursor
    74.             Select Case value
    75.                 Case ResizeDirection.Left
    76.                     _ctrl.Cursor = Cursors.SizeWE
    77.  
    78.                 Case ResizeDirection.Right
    79.                     _ctrl.Cursor = Cursors.SizeWE
    80.  
    81.                 Case ResizeDirection.Top
    82.                     _ctrl.Cursor = Cursors.SizeNS
    83.  
    84.                 Case ResizeDirection.Bottom
    85.                     _ctrl.Cursor = Cursors.SizeNS
    86.  
    87.                 Case ResizeDirection.BottomLeft
    88.                     _ctrl.Cursor = Cursors.SizeNESW
    89.  
    90.                 Case ResizeDirection.TopRight
    91.                     _ctrl.Cursor = Cursors.SizeNESW
    92.  
    93.                 Case ResizeDirection.BottomRight
    94.                     _ctrl.Cursor = Cursors.SizeNWSE
    95.  
    96.                 Case ResizeDirection.TopLeft
    97.                     _ctrl.Cursor = Cursors.SizeNWSE
    98.  
    99.                 Case Else
    100.                     _ctrl.Cursor = Cursors.Default
    101.             End Select
    102.         End Set
    103.     End Property
    104.  
    105. #Region " Functions and Constants "
    106.  
    107.     <DllImport("user32.dll")> _
    108.     Public Shared Function ReleaseCapture() As Boolean
    109.     End Function
    110.  
    111.     <DllImport("user32.dll")> _
    112.     Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
    113.     End Function
    114.  
    115.     Private Const WM_NCLBUTTONDOWN As Integer = &HA1
    116.     Private Const HTBORDER As Integer = 18
    117.     Private Const HTBOTTOM As Integer = 15
    118.     Private Const HTBOTTOMLEFT As Integer = 16
    119.     Private Const HTBOTTOMRIGHT As Integer = 17
    120.     Private Const HTCAPTION As Integer = 2
    121.     Private Const HTLEFT As Integer = 10
    122.     Private Const HTRIGHT As Integer = 11
    123.     Private Const HTTOP As Integer = 12
    124.     Private Const HTTOPLEFT As Integer = 13
    125.     Private Const HTTOPRIGHT As Integer = 14
    126.  
    127. #End Region
    128.  
    129. #Region " Moving & Resizing methods "
    130.  
    131.     Private Sub MoveControl(ByVal hWnd As IntPtr)
    132.         ReleaseCapture()
    133.         SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0)
    134.     End Sub
    135.  
    136.     Private Sub ResizeControl(ByVal hWnd As IntPtr, ByVal direction As ResizeDirection)
    137.         Dim dir As Integer = -1
    138.         Select Case direction
    139.             Case ResizeDirection.Left
    140.                 dir = HTLEFT
    141.             Case ResizeDirection.TopLeft
    142.                 dir = HTTOPLEFT
    143.             Case ResizeDirection.Top
    144.                 dir = HTTOP
    145.             Case ResizeDirection.TopRight
    146.                 dir = HTTOPRIGHT
    147.             Case ResizeDirection.Right
    148.                 dir = HTRIGHT
    149.             Case ResizeDirection.BottomRight
    150.                 dir = HTBOTTOMRIGHT
    151.             Case ResizeDirection.Bottom
    152.                 dir = HTBOTTOM
    153.             Case ResizeDirection.BottomLeft
    154.                 dir = HTBOTTOMLEFT
    155.         End Select
    156.  
    157.         If dir <> -1 Then
    158.             ReleaseCapture()
    159.             SendMessage(hWnd, WM_NCLBUTTONDOWN, dir, 0)
    160.         End If
    161.     End Sub
    162. #End Region
    163.  
    164. #Region " Event Handlers "
    165.  
    166.     Private Sub MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
    167.         If e.Location.X < BorderWidth And e.Location.Y < BorderWidth Then
    168.             If (Me.AllowResizeFlags And ResizeDirection.TopLeft) = ResizeDirection.TopLeft Then resizeDir = ResizeDirection.TopLeft
    169.  
    170.         ElseIf e.Location.X < BorderWidth And e.Location.Y > _ctrl.Height - BorderWidth Then
    171.             If (Me.AllowResizeFlags And ResizeDirection.BottomLeft) = ResizeDirection.BottomLeft Then resizeDir = ResizeDirection.BottomLeft
    172.  
    173.         ElseIf e.Location.X > _ctrl.Width - BorderWidth And e.Location.Y > _ctrl.Height - BorderWidth Then
    174.             If (Me.AllowResizeFlags And ResizeDirection.BottomRight) = ResizeDirection.BottomRight Then resizeDir = ResizeDirection.BottomRight
    175.  
    176.         ElseIf e.Location.X > _ctrl.Width - BorderWidth And e.Location.Y < BorderWidth Then
    177.             If (Me.AllowResizeFlags And ResizeDirection.TopRight) = ResizeDirection.TopRight Then resizeDir = ResizeDirection.TopRight
    178.  
    179.         ElseIf e.Location.X < BorderWidth Then
    180.             If (Me.AllowResizeFlags And ResizeDirection.Left) = ResizeDirection.Left Then resizeDir = ResizeDirection.Left
    181.  
    182.         ElseIf e.Location.X > _ctrl.Width - BorderWidth Then
    183.             If (Me.AllowResizeFlags And ResizeDirection.Right) = ResizeDirection.Right Then resizeDir = ResizeDirection.Right
    184.  
    185.         ElseIf e.Location.Y < BorderWidth Then
    186.             If (Me.AllowResizeFlags And ResizeDirection.Top) = ResizeDirection.Top Then resizeDir = ResizeDirection.Top
    187.  
    188.         ElseIf e.Location.Y > _ctrl.Height - BorderWidth Then
    189.             If (Me.AllowResizeFlags And ResizeDirection.Bottom) = ResizeDirection.Bottom Then resizeDir = ResizeDirection.Bottom
    190.  
    191.         Else
    192.             resizeDir = ResizeDirection.None
    193.         End If
    194.     End Sub
    195.  
    196.     Private Sub MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
    197.         If e.Button = Windows.Forms.MouseButtons.Left Then
    198.             If resizeDir = ResizeDirection.None Then
    199.                 If Me.AllowMoving Then MoveControl(_ctrl.Handle)
    200.             Else
    201.                 If (Me.AllowResizeFlags And resizeDir) = resizeDir Then ResizeControl(_ctrl.Handle, resizeDir)
    202.             End If
    203.         End If
    204.     End Sub
    205.  
    206. #End Region
    207.  
    208. End Class
    Attached Files Attached Files

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    I would just like to point out an observation I just made. It appears that there is a hardcoded minimum size for both forms and controls resized this way. Try to resize your browser window as small as you can. You will see that the minimum you can make it is as high as the caption bar, and as wide as the caption bar when only one letter of the caption is displayed.
    The same limit applies to any control resized using the method in the class above.

    That this limit exists for forms is probably a good thing, but for controls I can imagine it giving unexplainable trouble. Hence, I decided to mention it. So now you know

  4. #4
    Hyperactive Member
    Join Date
    Jun 2007
    Posts
    445

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    The Moving and Resizing a control a Control coding everything work perfect to me!!
    But only the problem is if want to make the button move-able... the button cannot click and perform it's function.........

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Can you try handling the MouseDown or MouseUp event instead of the Click event? Maybe that will work. It's not exactly the same as the Click event, but it may work for you.

    If not, my only other idea is to create a UserControl, apply the movable class to that UserControl, and then put a Button on the UserControl, in the center. Leave a border around the Button where you can see the background of the UserControl. If all is well, the user should be able to move and resize the button using the edge of the Usercontrol, but still be able to click the button.
    Of course, the Button's properties must be exposed via properties if you want to use them, and you also need to expose any events the button receives.

  6. #6
    Registered User
    Join Date
    Jul 2009
    Posts
    71

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Thanks, I didn't know using api would be faster than 'normal' resizing (doing calculations and use it with Me.Size) but this is really useful as a free ribbon control available on the internet has a lot of flickering and consumes almost all CPU because it is invalidating itself every time, and the resize method probably is not done with api.

  7. #7
    Hyperactive Member
    Join Date
    Jun 2007
    Posts
    445

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    I realize that it fail to move Label control...
    anyone move?

  8. #8

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    I noticed that too. I've no idea why that happens. The label control must have something different. If it is crucial that you can move a label, you can also use a PictureBox, and simply paint the text yourself in the Paint event using the DrawString method. Dragging a Picturebox works fine.

  9. #9
    Frenzied Member
    Join Date
    Aug 2000
    Posts
    1,090

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Pretty slick, but one problem off the bat is that auto scrolling doesn't work. For example, when I set the Panel to AutoScroll = True, and I drag the progressbar off the side, the scrollbar doesn't automatically appear.

    Visual Studio 2010

  10. #10
    Stack Overflow mod​erator
    Join Date
    May 2008
    Location
    British Columbia, Canada
    Posts
    2,824

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Take a look at the WndProc in here:
    http://www.vbforums.com/showthread.php?t=596447
    It seems to be a prettier way to make forms moveable & resizeable with all the confusing goodness of windows messages but no APIs!

  11. #11

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Quote Originally Posted by minitech View Post
    Take a look at the WndProc in here:
    http://www.vbforums.com/showthread.php?t=596447
    It seems to be a prettier way to make forms moveable & resizeable with all the confusing goodness of windows messages but no APIs!
    Yes, I know about that. I don't see how it's prettier though. It comes down to the same thing. Whether I use SendMessage or override WndProc, I still need all the constants and stuff.

    Also, I can't use the WndProc method for moving controls, because I'm not overriding any controls. With my MoveControl class you can simply specify which control you want to enable moving for and that's that.

  12. #12
    PowerPoster JuggaloBrotha's Avatar
    Join Date
    Sep 2005
    Location
    Lansing, MI; USA
    Posts
    3,817

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Just implemented this code in one of my apps this morning Nick, I prefer this api method as it allows me to move controls in the same manor (same code). Works wonderfully.

  13. #13
    PowerPoster JuggaloBrotha's Avatar
    Join Date
    Sep 2005
    Location
    Lansing, MI; USA
    Posts
    3,817

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Well I've discovered that you don't even need a control on the form to drag the borderless window around. Just define a Rectangle and in the MouseDown event check that rectangle if it contains the mouse coord's and to the drag thing, otherwise let the resize check happen. Here's an example, it defines a rectangle spanning the entire top of the form, 30px in height (minus the resize border width of course)
    Code:
        Private Const m_TitleHeight As Integer = 30I
    
        Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
            If Me.DragRect.Contains(e.Location) Then
                ReleaseCapture()
                SendMessage(Me.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0I)
            Else
                If e.Button = MouseButtons.Left And Me.WindowState <> FormWindowState.Maximized Then ResizeForm(ResizeDir)
            End If
        End Sub
    
        Private ReadOnly Property DragRect() As Rectangle
            Get
                Return New Rectangle(m_BorderWidth, m_BorderWidth, Me.ClientSize.Width - (m_BorderWidth * 2I), m_TitleHeight)
            End Get
        End Property
    You can still resize the window from the top edge vertically, diagonally, or horizontally respectively because the rectangle's bounds take that into account when it's created.

  14. #14
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    2,441

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Practically all the problems of borderless form dragging are now sorted out! But there is still one thing to be dealt with. The smooth-dragging methods work by letting the form or a selected rectangle behave like a title bar. You can drag the form as far as you like offscreen to the left, right or bottom. But not to the top! Windows Forms apparently don't like having their title bars moved off the top of the screen. You can drag the form up, but it bounces back down again as soon as you release the mouse button.

    I wanted to change this behaviour so I spent some time looking at Windows messages. I found that the bounce-down behaviour was uniquely associated with a WM_CAPTURECHANGED message immediately followed by a WM_WINDOWPOSCHANGING. Then I managed to overcome the bounce with the following WndProc Sub:

    Code:
     Const WM_CAPTURECHANGED As Integer = &H215
        Const WM_WINDOWPOSCHANGING As Int32 = &H46
        Const SWP_NOMOVE As Integer = &H2
      
    <StructLayout(LayoutKind.Sequential, Pack:=1)> _
        Public Structure WINDOWPOS
            Public hwnd As Int32
            Public hWndInsertAfter As Int32
            Public x As Int32
            Public y As Int32
            Public cx As Int32
            Public cy As Int32
            Public flags As Int32
        End Structure
    
        Private dontMove As Boolean
    
        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            If Me.Top < 0 Then
                If m.Msg = WM_CAPTURECHANGED Then
                    dontMove = True
                ElseIf m.Msg = WM_WINDOWPOSCHANGING AndAlso dontMove = True Then
                    Dim wPos As WINDOWPOS = DirectCast _
                      (Runtime.InteropServices.Marshal.PtrToStructure _
                      (m.LParam, GetType(WINDOWPOS)), WINDOWPOS)
                    wPos.flags = wPos.flags Or SWP_NOMOVE
                    Runtime.InteropServices.Marshal.StructureToPtr(wPos, m.LParam, True)
                    dontMove = False
                Else
                    dontMove = False
                End If
            End If
            MyBase.WndProc(m)
        End Sub
    The above code can simply be added to any form using your dragging method without further changes. Since you prefer using APIs to WndProc, you may wish to consider whether this can be translated into an API version.

    BB
    Last edited by boops boops; Jun 22nd, 2010 at 12:50 PM. Reason: WINDOWPOS declaration added

  15. #15
    PowerPoster JuggaloBrotha's Avatar
    Join Date
    Sep 2005
    Location
    Lansing, MI; USA
    Posts
    3,817

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Quote Originally Posted by boops boops View Post
    Practically all the problems of borderless form dragging are now sorted out! But there is still one thing to be dealt with. The smooth-dragging methods work by letting the form or a selected rectangle behave like a title bar. You can drag the form as far as you like offscreen to the left, right or bottom. But not to the top! Windows Forms apparently don't like having their title bars moved off the top of the screen. You can drag the form up, but it bounces back down again as soon as you release the mouse button.
    I haven't used a Windows OS where that would happen. The closest I've come across so far is in Win7 if you're moving a window up and the cursor enters the top screen zone it'll suggest maximizing the form, but right here in WinXP (I'm at work) I can have a window with the title bar partially off the screen at the top there (only the bottom half of the close button is visible right now)

  16. #16
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    2,441

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Yes, you can always drag a title bar to about 20 pixels above the top. But have you succeeded in dragging a borderless form any further than that, the same way as you can to left, right or bottom? BB

  17. #17
    PowerPoster JuggaloBrotha's Avatar
    Join Date
    Sep 2005
    Location
    Lansing, MI; USA
    Posts
    3,817

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Quote Originally Posted by boops boops View Post
    Yes, you can always drag a title bar to about 20 pixels above the top. But have you succeeded in dragging a borderless form any further than that, the same way as you can to left, right or bottom? BB
    Good point, I guess I'd never noticed that with borderless forms

  18. #18

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    There's a good reason for windows not allowing windows to go that high: you cannot grab the title bar to move them anymore... Obviously that does not need to apply to a borderless form, but since windows cannot know what area of your form you are using as the dragging part, it just enforces the same rule.

  19. #19
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    2,441

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Quote Originally Posted by NickThissen View Post
    There's a good reason for windows not allowing windows to go that high: you cannot grab the title bar to move them anymore... Obviously that does not need to apply to a borderless form, but since windows cannot know what area of your form you are using as the dragging part, it just enforces the same rule.
    Are you saying that Windows is stupider than we are? That would take some doing. BB

  20. #20

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Well think about it... Windows may know whether a window is borderless or not, but it cannot possibly tell what region you have decided, in your code, to use as the dragging region. I am referring to JB's update on this, but it goes just as well for my original approach. So, since it cannot know that your resizing region may not be completely invisible if the window is at a certain height, it must conclude that there is a chance that moving the form that high would make it impossible to move it any further later. At least I assume that is the thought that went into this design. It's just the default window behavior in windows and goes for all windows, even borderless ones.

  21. #21
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    2,441

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Sorry, I didn't mean my last comment to be taken too seriously -- I just couldn't find the right smiley. Of course it was a logical design decision to prevent title bars being dragged so high you couldn't pull them back again. Sometimes you want to keep that behaviour for a borderless form; for example, if you have designed a custom title bar at the top. And there are plenty of other situations where it won't matter much. All the same, it is not consistent for a borderless form because it should be equally draggable in all directions.

    I have been using this form dragging code (or its WndProc equivalent) for dragging Layered Windows for the purpose of compositing large partly-transparent images. In that case, the bounce behaviour is undesirable. That is why I tried to find a way of stopping it.

    The WndProc and API codes for borderless form dragging have been in circulation for a few years now. But nobody seems to have noticed the "bouncing off the top" problem or thought it worth mentioning. I hope I have contributed something useful to this thread by pointing out the problem and offering a solution.

    BB

  22. #22
    Lively Member Amerigo's Avatar
    Join Date
    Dec 2008
    Location
    PSR B1620-26 c-1
    Posts
    121

    Talking Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Thank you very much, NickThissen! If I knew what you looked like, I would make an idle of you to worship. I guess a shrine will have to do...
    Last edited by Amerigo; Aug 23rd, 2010 at 11:16 AM.
    Anyone who does not wonder, is either omnipotent or a fool.
    Amerigoware <<<My Projects

  23. #23
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    This code is great! It is so much smoother than trying to resize things through code! I have two questions though:

    1. Is it possible to get the MoveControl to consistently resize when the control is within a TableLayoutPanel? I was able to get a Panel contained within a cell of a TableLayoutPanel to resize by setting the Panel's Anchor to All, while setting the TableLayoutPanel Row and Column that contain the Panel to AutoSize.

    However, I've found that the ability to resize the Panel stops working if the TableLayoutPanel's size is changed to a value that is different than the compiled value. Strangely though, if the size is set back to the compiled size, the ability to resize is back!

    2. Is there a way to utilize a control's Minimum and Maximum sizes when resizing the control? I've noticed that while resizing or moving a control on a form the cursor is prevented from leaving the form. I think this is great, because it prevents a user from moving a control "off" the form. However, controls can still be resized to cover other controls. I thought I'd try and prevent this by setting the Max and Min Sizes, but the MoveControl class doesn't seem to check those values when resizing.

    Again, this is great code, I was just curious about some things. Thanks.

  24. #24

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    1. This is probably due to the TableLayoutPanel taking control of the sizing in some way. I have no clue how to get around it, if it's possible at all.

    2. Apparently the system doesn't check the minimum and maximum size of controls in the same way it does for forms. My code works by treating the control as a window and utilizes the system moving and resizing messages. The result is that the moving and resizing works as optimized as possible (this is why it's so smooth), but at the same time you have very little control over it. For example you can't go below a hardcoded minimum size (the same minimum size that every window in windows gets, try resizing your browser window as small as you can), this seems to be wired into the system somehow and I don't think there's any way around it.

    Long story short: if the system ignores the minimum / maximum size properties of the controls, I doubt there's a way around it. You can try catching Resizing events (though I doubt they will be raised) and reset the value there, perhaps...

  25. #25
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    I was afraid of that. I'll do some more research and see if I can mix and match higher level .NET "bounds" checks with your API calls. I'll get back to you sometime in the coming week.

  26. #26
    PowerPoster JuggaloBrotha's Avatar
    Join Date
    Sep 2005
    Location
    Lansing, MI; USA
    Posts
    3,817

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    arcanine, your best bet is probably going to be handling the MouseDown, MouseMove and MouseUp events of the window and do the move/resize yourself because you can control all aspects of it, like when moving around you can code it to have your borderless forms snap to each other or to the edge of the screen and when it comes to resizing you can take into consideration not only your form's MinimumSize property but the sizes of all the controls on the form too so the form, despite it's MinimumSize property, is never sized too small.

    Ideally you'd figure out what the absolute minimum size of the form should be and put that into the MinimumSize property so you don't have it calculating all that stuff for each control on your form in real time, while it's resizing, that will help with the speed/smoothness of the resize too. This applies to whether you use the API or handle those actions yourself.

  27. #27
    Member
    Join Date
    Oct 2011
    Location
    Sri Lanka
    Posts
    40

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Quote Originally Posted by NickThissen View Post
    Hi,

    Here is a small example of how to move and resize a form without a border (FormBorderStyle = None).

    Instead of manually setting the Location and Size of the Form, I have decided to use the SendMessage function to handle it. This results in a much smoother experience, (especially with lots of controls) and will also make sure that the form always follows the mouse, regardless of how fast you move it. In other examples you will often notice that if you move your mouse too fast, the form can no longer keep up and will stop moving.


    The example uses a Panel as the form's caption. Dragging the panel will move the form, just like the real caption of a form with a border would behave.
    You can of course decide to use a different control. You could even use the form itself. I just thought a Panel was the easiest example.

    For resizing, I did not use any special controls; I merely use a BorderWith constant (default: 6) that defines the width of the area you can use to resize the form.

    Screenshot:

    (You can of course use nice icons for the buttons instead, but that was not the purpose of this codebank submission)



    The form moving works pretty simple: when the MouseDown event of the Panel is fired, a simple check is done to make sure that the user clicked the Left button instead of the Right button, and that the form is not maximized (you cannot move a maximized form).
    Then, a MoveForm() method is called, in which the SendMessage function is called with the correct parameters.
    vb.net Code:
    1. Private Sub pnlCaption_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pnlCaption.MouseDown
    2.         If e.Button = Windows.Forms.MouseButtons.Left And Me.WindowState <> FormWindowState.Maximized Then
    3.             MoveForm()
    4.         End If
    5.     End Sub
    6.  
    7.     Private Sub MoveForm()
    8.         ReleaseCapture()
    9.         SendMessage(Me.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0)
    10.     End Sub


    The resizing is a little more involved, as it requires a check for the position of the mouse cursor, and changes the cursor to a resizing cursor.
    For this, I made a Property resizeDir which changes the cursor depending on which value it gets automatically.
    The value of this property is used in a ResizeForm method, which calls the SendMessage function again with the correct parameters (depending on the position of the mouse, or the resizeDir value).
    vb.net Code:
    1. Private Sub Form1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
    2.         If e.Button = Windows.Forms.MouseButtons.Left And Me.WindowState <> FormWindowState.Maximized Then
    3.             ResizeForm(resizeDir)
    4.         End If
    5.     End Sub
    6.  
    7.     Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
    8.         'Calculate which direction to resize based on mouse position
    9.  
    10.         If e.Location.X < BorderWidth And e.Location.Y < BorderWidth Then
    11.             resizeDir = ResizeDirection.TopLeft
    12.  
    13.         ElseIf e.Location.X < BorderWidth And e.Location.Y > Me.Height - BorderWidth Then
    14.             resizeDir = ResizeDirection.BottomLeft
    15.  
    16.         ElseIf e.Location.X > Me.Width - BorderWidth And e.Location.Y > Me.Height - BorderWidth Then
    17.             resizeDir = ResizeDirection.BottomRight
    18.  
    19.         ElseIf e.Location.X > Me.Width - BorderWidth And e.Location.Y < BorderWidth Then
    20.             resizeDir = ResizeDirection.TopRight
    21.  
    22.         ElseIf e.Location.X < BorderWidth Then
    23.             resizeDir = ResizeDirection.Left
    24.  
    25.         ElseIf e.Location.X > Me.Width - BorderWidth Then
    26.             resizeDir = ResizeDirection.Right
    27.  
    28.         ElseIf e.Location.Y < BorderWidth Then
    29.             resizeDir = ResizeDirection.Top
    30.  
    31.         ElseIf e.Location.Y > Me.Height - BorderWidth Then
    32.             resizeDir = ResizeDirection.Bottom
    33.  
    34.         Else
    35.             resizeDir = ResizeDirection.None
    36.         End If
    37.     End Sub
    38.  
    39. Private Sub ResizeForm(ByVal direction As ResizeDirection)
    40.         Dim dir As Integer = -1
    41.         Select Case direction
    42.             Case ResizeDirection.Left
    43.                 dir = HTLEFT
    44.             Case ResizeDirection.TopLeft
    45.                 dir = HTTOPLEFT
    46.             Case ResizeDirection.Top
    47.                 dir = HTTOP
    48.             Case ResizeDirection.TopRight
    49.                 dir = HTTOPRIGHT
    50.             Case ResizeDirection.Right
    51.                 dir = HTRIGHT
    52.             Case ResizeDirection.BottomRight
    53.                 dir = HTBOTTOMRIGHT
    54.             Case ResizeDirection.Bottom
    55.                 dir = HTBOTTOM
    56.             Case ResizeDirection.BottomLeft
    57.                 dir = HTBOTTOMLEFT
    58.         End Select
    59.  
    60.         If dir <> -1 Then
    61.             ReleaseCapture()
    62.             SendMessage(Me.Handle, WM_NCLBUTTONDOWN, dir, 0)
    63.         End If
    64.     End Sub


    I attached the full example (without any compiled files, so you will probably need to Build it first) so you can play around with it.

    Enjoy!
    Use this code to move the form.Its simpler

    Code:
     Private Const WM_NCHITTEST As Integer = &H84
        Private Const HTCLIENT As Integer = &H1
        Private Const HTCAPTION As Integer = &H2
    
        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            Select Case m.Msg
                Case WM_NCHITTEST
                    MyBase.WndProc(m)
                    If m.Result = HTCLIENT Then
                        m.Result = HTCAPTION
                    End If
                Case Else
                    'Make sure you pass unhandled messages back to the default message handler.
                    MyBase.WndProc(m)
            End Select
        End Sub

  28. #28

  29. #29
    Stack Overflow mod​erator
    Join Date
    May 2008
    Location
    British Columbia, Canada
    Posts
    2,824

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    It's a little more efficient, but that's not the point. It's a lot less flexible.

  30. #30
    Member
    Join Date
    Oct 2011
    Location
    Sri Lanka
    Posts
    40

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Quote Originally Posted by NickThissen View Post
    It comes down to the same thing in the end. Why is it simpler? It's more code and harder to understand imo.
    Im not questioning your code or your knowledge.I just posted a more memory efficient method

  31. #31
    Member
    Join Date
    Oct 2011
    Location
    Sri Lanka
    Posts
    40

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    Quote Originally Posted by NickThissen View Post
    It comes down to the same thing in the end. Why is it simpler? It's more code and harder to understand imo.
    Your code needs a panel or some other component to work on.The code I posted does not need any component.Just paste the code in the form class and then you can move your form

  32. #32

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    It doesn't need a panel, you can just as well call my MoveForm code in the MouseClick of the Form itself. A Panel is just a neat example because that's a typical way to mimic a titlebar (you usually can't move a form from anywhere except the titlebar).

    I also wasn't shooting down your code or anything, it's just as nice because it basically comes down to the same thing. It's just a little less flexible as minitech mentions (what if the user does want to use a panel or other control to move the form?) and I think it is less easy to understand. In your case, a method called "WndProc" (which is not very descriptive of what it does in this case, and I know you can't help that cause you override it) which checks a 'Result' to some arbitrary values. In my case, a MouseClick event that calls a MoveForm method. I think in most cases especially less experienced people will understand what my code does even if they don't understand how, which might not be true for your code immediately (though some comments can always fix that).

    But thanks for the contribution, always appreciated

  33. #33
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Move and Resize a Control or a Borderless Form - using window messages (smooth!)

    I hate to resurrect an old thread, but I've started fooling with this code again. Pertaining to moving a control within a form, I've noticed that the cursor cannot be moved outside of the client rectangle of the form that the control is on. Does anyone know how windows determines the clip rectangle in this case? I feel that if we can specify the clip rectangle (maybe using ClipCursor) then we can handle the control's Min/Max size. If we are able to set the cursor clip, it would only take a little logic and math to get the clip rectangle right.

    Edit

    So I've managed to use the user32.dll ClipCursor function to limit the movement of the mouse. However, when your code calls the SendMessage function, the ClipCursor bounds are reset. I know almost nothing about the user32 functions, so I'm really over my head on this stuff. I'm guessing though, by the other posts, that it's just not possible. With my quick skim of the MSDN articles pertaining to user32.dll functions, it seems like it should be possible.

    Excerpts from your code (without the ClipCursor stuff, since it didn't work):
    Code:
        Private Sub MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
            If e.Button = Windows.Forms.MouseButtons.Left Then
                If resizeDir = ResizeDirection.None Then
                    If Me.AllowMoving Then MoveControl(_ctrl.Handle)
                Else
                    If (Me.AllowResizeFlags And resizeDir) = resizeDir Then
                        ResizeControl(_ctrl.Handle, resizeDir)
                    End If
                End If
            End If
        End Sub
    
        Private Sub ResizeControl(ByVal hWnd As IntPtr, ByVal direction As ResizeDirection)
            Dim dir As Integer = -1
    
            Select Case direction
                Case ResizeDirection.Left
                    dir = HTLEFT
                Case ResizeDirection.TopLeft
                    dir = HTTOPLEFT
                Case ResizeDirection.Top
                    dir = HTTOP
                Case ResizeDirection.TopRight
                    dir = HTTOPRIGHT
                Case ResizeDirection.Right
                    dir = HTRIGHT
                Case ResizeDirection.BottomRight
                    dir = HTBOTTOMRIGHT
                Case ResizeDirection.Bottom
                    dir = HTBOTTOM
                Case ResizeDirection.BottomLeft
                    dir = HTBOTTOMLEFT
            End Select
    
            If dir <> -1 Then
                ReleaseCapture()
                SendMessage(hWnd, WM_NCLBUTTONDOWN, dir, 0)
            End If
        End Sub
    Edit

    No ideas?
    Last edited by arcanine; Mar 16th, 2013 at 03:31 PM. Reason: Shameless bump.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.