Restricting movement of an mdi child

Briinnok

Member
Joined
Feb 6, 2008
Messages
8
Programming Experience
10+
I am attempting to restrict movement of an mdi child window to the bounds of its parent. This is how I am doing it now...

VB.NET:
    Private Sub frmUpdateTime_Move(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Move

        If Me.Left < 0 Then Me.Left = 0
        If Me.Top < 0 Then Me.Top = 0
        If Me.Left > Me.Parent.ClientSize.Width - Me.Width Then Me.Left = Me.Parent.ClientSize.Width - Me.Width
        If Me.Top > Me.Parent.ClientSize.Height - Me.Height Then Me.Top = Me.Parent.ClientSize.Height - Me.Height

    End Sub

This works, but it seems that the mouse keeps trying to move it as it goes past the bounds. As I attempt to drag it further past the bounds, it seems to be drawing one frame at the bad position before calling the me.move event.

A. Is there a way to trap the movement before the form is drawn?

B. Am I an idiot, and is there some freaking easy way to do this that I am missing?

(Probably B)
 
This stops the movement before it happens:
VB.NET:
Const WM_WINDOWPOSCHANGING As Int32 = &H46
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

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If m.Msg = WM_WINDOWPOSCHANGING Then
        Dim pos As WINDOWPOS = Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(WINDOWPOS))
        If pos.x < 0 Then
            pos.x = 0
        ElseIf pos.x > Me.Parent.ClientSize.Width - Me.Width Then
            pos.x = Me.Parent.ClientSize.Width - Me.Width
        End If
        If pos.y < 0 Then
            pos.y = 0
        ElseIf pos.y > Me.Parent.ClientSize.Height - Me.Height Then
            pos.y = Me.Parent.ClientSize.Height - Me.Height
        End If
        Runtime.InteropServices.Marshal.StructureToPtr(pos, m.LParam, True)
    End If
    MyBase.WndProc(m)
End Sub
 
This stops the movement before it happens:
VB.NET:
Const WM_WINDOWPOSCHANGING As Int32 = &H46
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

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If m.Msg = WM_WINDOWPOSCHANGING Then
        Dim pos As WINDOWPOS = Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(WINDOWPOS))
        If pos.x < 0 Then
            pos.x = 0
        ElseIf pos.x > Me.Parent.ClientSize.Width - Me.Width Then
            pos.x = Me.Parent.ClientSize.Width - Me.Width
        End If
        If pos.y < 0 Then
            pos.y = 0
        ElseIf pos.y > Me.Parent.ClientSize.Height - Me.Height Then
            pos.y = Me.Parent.ClientSize.Height - Me.Height
        End If
        Runtime.InteropServices.Marshal.StructureToPtr(pos, m.LParam, True)
    End If
    MyBase.WndProc(m)
End Sub
Not just A man but THE man.
 
:) much can be done with Windows applications that isn't in the .Net Framework library yet.
 
Of course, there is a serious bug, with that code Maximize isn't possible. :eek:
Here's the fix, you need these declarations in addition:
VB.NET:
Public Const WM_GETMINMAXINFO As Int32 = &H24
Structure MINMAXINFO
    Public ptReserved As POINTAPI
    Public ptMaxSize As POINTAPI
    Public ptMaxPosition As POINTAPI
    Public ptMinTrackSize As POINTAPI
    Public ptMaxTrackSize As POINTAPI
End Structure
Structure POINTAPI
    Public x As Int32
    Public y As Int32
End Structure
Then this modified WndProc gets max window metrics and check first that requested window position doesn't correspond to these:
VB.NET:
Private mm As MINMAXINFO
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    Select Case m.Msg
        Case WM_GETMINMAXINFO
            mm = Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(MINMAXINFO))

        Case WM_WINDOWPOSCHANGING                
            Dim pos As WINDOWPOS = Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(WINDOWPOS))
            If pos.x <> mm.ptMaxPosition.x AndAlso pos.y <> mm.ptMaxPosition.y Then
                If pos.x < 0 Then
                    pos.x = 0
                    Runtime.InteropServices.Marshal.StructureToPtr(pos, m.LParam, True) 
                ElseIf pos.x > Me.Parent.ClientSize.Width - Me.Width Then
                    pos.x = Me.Parent.ClientSize.Width - Me.Width
                    Runtime.InteropServices.Marshal.StructureToPtr(pos, m.LParam, True) 
                End If
                If pos.y < 0 Then
                    pos.y = 0
                    Runtime.InteropServices.Marshal.StructureToPtr(pos, m.LParam, True) 
                ElseIf pos.y > Me.Parent.ClientSize.Height - Me.Height Then
                    pos.y = Me.Parent.ClientSize.Height - Me.Height
                    Runtime.InteropServices.Marshal.StructureToPtr(pos, m.LParam, True) 
                End If
            End If
    End Select
    MyBase.WndProc(m)
End Sub
 
That is awesome, guys! Thanks for the help.

For the future, though, if I needed to dig this stuff up on my own, is there a good place to find this? I'm assuming that you are overriding the inherent handler for drawing the window here. That's not exactly the kind of things I'm finding in the n00b help stuff for vb.net I've been using.
 
There are a lot of things that are hard to find unless you have a very specific idea of what key words to search for. I was well aware of the WndProc method and how it can be used, but I'd never seen the WM_WINDOWCHANGING message used before. Some information you will happen on when you're not specifically looking for it, which is why it's important to read a lot. Other things you can ferret out by thinking laterally. For instance, now that you know how to use the WndProc method you might look for a list of Windows messages you can listen for. It's highly likely that there will come a time in the future where you'll have to do this again so being aware of the sorts of things you can do with it is a good idea.
 
Back
Top