Keeping Window on the Screen

I would like to allow a user to move a window but not to drag it off the screen.

I dont want to use a timer to check the window state and I have tried to do this with the paint event but paint doesnt fire when the window is being dragged off, only on.

I have looked but fail to see an API to limit the window drag area.

Thanks for any help.
woodsrrAsked:
Who is Participating?
 
DennisBorgConnect With a Mentor Commented:
(first a quick note -- when subclassing your form this way, you cannot break your code and trace line-by-line. You also cannot use the Stop button on the VB menubar. Doing so while you have activated subclassing will cause your program to crash. *ALWAYS* save your work before running/testing the program)


To subclass your form, you need to add the following to a Standard Code Module:


-----------------------------------------------------------
Option Explicit

Public OldWinProc As Long
Public OldhWnd    As Long

Public Const GWL_WNDPROC = (-4)
Public Const WM_MOVE = &H3

Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
   ByVal hWnd As Long, _
   ByVal nIndex As Long, _
   ByVal dwNewLong As Long _
) As Long

Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" ( _
   ByVal lpPrevWndFunc As Long, _
   ByVal hWnd As Long, _
   ByVal Msg As Long, _
   ByVal wParam As Long, _
   ByVal lParam As Long _
) As Long

Public Declare Function DefWindowProc Lib "user32" Alias "DefWindowProcA" ( _
   ByVal hWnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   ByVal lParam As Long _
) As Long

Function MyWinProc(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
   Select Case wMsg:
      Case WM_MOVE:
         With Form1
            If .Left + .Width > Screen.Width Then .Left = Screen.Width - .Width
            If .Left < 0 Then .Left = 0
            If .Top + .Height > Screen.Height Then .Top = Screen.Height - .Height
            If .Top < 0 Then .Top = 0
         End With
      Case Else:
         MyWinProc = CallWindowProc(OldWinProc, hWnd, wMsg, wParam, lParam)
   End Select
End Function

Sub SubClassWin(ByVal hWnd As Long)
   OldhWnd = hWnd
   OldWinProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf MyWinProc)
End Sub


Sub UnSubClassWin()
   SetWindowLong OldhWnd, GWL_WNDPROC, OldWinProc
End Sub
-----------------------------------------------------------




Then, in your form's Load event:

   SubClassWin Me.hWnd



And in your form's Unload event:

   UnSubClassWin


-Dennis Borg
0
 
withAtwistCommented:
.
0
 
DennisBorgCommented:
There is a window message you can trap, which you can use to restrict the window's minimum and maximum size, so that the user cannot resize the window too small or too big. I'm not sure, but I think that within that same mechanism, you can specify the limits of where the form can be moved within.

But there is another method you can use; either way, you have to subclass your window.

This other method would be to trap for the WM_MOVE message, and then check to see if the form is off the edge of the screen. For example:

         With Form1
            If .Left + .Width > Screen.Width Then .Left = Screen.Width - .Width
            If .Left < 0 Then .Left = 0
            If .Top + .Height > Screen.Height Then .Top = Screen.Height - .Height
            If .Top < 0 Then .Top = 0
         End With


Are you familiar with subclassing, and how you'd go about that?


-Dennis Borg
0
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

 
Friedhelm Feller-PrzybylSoftware EngineerCommented:
maybe not what you want...
If you used an mdi-form the window that is an mdi child can not be moved outside the mdi form
0
 
DennisBorgCommented:
jendrix:

>maybe not what you want...
>If you used an mdi-form the window that is an mdi child
>can not be moved outside the mdi form

I don't think that MDI Parent windows and MDI Child windows are being used here.

woodsrr does describe the form being dragged off the *screen*, not off the parent form.


-Dennis Borg
0
 
woodsrrAuthor Commented:
Trapping the window move message is what I need, not clear on how to do that, but will look into it.
0
 
woodsrrAuthor Commented:
Trapping the window move message is what I need, not clear on how to do that, but will look into it.
0
 
woodsrrAuthor Commented:
Ok, I read that subclassinc would require me to create a DLL using C or C++ or a third party package such as Desaware's Spy.  Since my C is rusty at best, what if any probles are there in using the Desaware Spy, or is there a better way?
0
 
Richie_SimonettiIT OperationsCommented:
You can subclass without any external dll(almost).
0
 
woodsrrAuthor Commented:
Ok, I read that subclassinc would require me to create a DLL using C or C++ or a third party package such as Desaware's Spy.  Since my C is rusty at best, what if any probles are there in using the Desaware Spy, or is there a better way?
0
 
woodsrrAuthor Commented:
They way I have works pretty good, was just looking for a better way.  Thought there must be an API that does this?  My window doesnt have a title bar anyways so they have to click and drag on the form.  Here is how I'm doing it now.

Dim miX As Integer
Dim miY As Integer

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        miX = X
        miY = Y
    End If
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        If (Me.Left + X - miX > 0) Then
            If (Me.Left + X + Me.Width - miX < Screen.Width) Then
                Me.Left = Me.Left + (X - miX)
            Else
                Me.Left = Screen.Width - Me.Width
            End If
        Else
            Me.Left = 0
        End If
       
        If (Me.Top + Y - miY > 0) Then
            If (Me.Top + Y + Me.Height - miY < Screen.Height) Then
                Me.Top = Me.Top + (Y - miY)
            Else
                Me.Top = Screen.Height - Me.Height
            End If
        Else
            Me.Top = 0
        End If
    End If
End Sub
0
 
DennisBorgCommented:
woodsrr:

Which version of VB are you using? If you have VB5 or VB6, you can subclass your own window without third-party controls or DLL's (via the AddressOf operator, SetWindowLong(), and some code in a Standard Code Module).

-Dennis Borg
0
 
DennisBorgCommented:
Of course, in the code below, you'd want to change "Form1" to the actual name of your form.

     Case WM_MOVE:
        With Form1
           If .Left + .Width > Screen.Width Then .Left = Screen.Width - .Width
           If .Left < 0 Then .Left = 0
           If .Top + .Height > Screen.Height Then .Top = Screen.Height - .Height
           If .Top < 0 Then .Top = 0
        End With


0
 
woodsrrAuthor Commented:
They way I have works pretty good, was just looking for a better way.  Thought there must be an API that does this?  My window doesnt have a title bar anyways so they have to click and drag on the form.  Here is how I'm doing it now.

Dim miX As Integer
Dim miY As Integer

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        miX = X
        miY = Y
    End If
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        If (Me.Left + X - miX > 0) Then
            If (Me.Left + X + Me.Width - miX < Screen.Width) Then
                Me.Left = Me.Left + (X - miX)
            Else
                Me.Left = Screen.Width - Me.Width
            End If
        Else
            Me.Left = 0
        End If
       
        If (Me.Top + Y - miY > 0) Then
            If (Me.Top + Y + Me.Height - miY < Screen.Height) Then
                Me.Top = Me.Top + (Y - miY)
            Else
                Me.Top = Screen.Height - Me.Height
            End If
        Else
            Me.Top = 0
        End If
    End If
End Sub
0
 
akkalamCommented:
Hi,
 u can do this by using Timer control. Set Timer's Interverl to 3 or 4. And write the code in it's event.
With Form1
 If .Left + .Width > Screen.Width Then .Left = Screen.Width - .Width
 If .Left < 0 Then .Left = 0
 If .Top + .Height > Screen.Height Then .Top = Screen.Height - .Height
 If .Top < 0 Then .Top = 0
 End With

I am not sure, u just check out
Thanks

0
 
woodsrrAuthor Commented:
They way I have works pretty good, was just looking for a better way.  Thought there must be an API that does this?  My window doesnt have a title bar anyways so they have to click and drag on the form.  Here is how I'm doing it now.

Dim miX As Integer
Dim miY As Integer

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        miX = X
        miY = Y
    End If
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        If (Me.Left + X - miX > 0) Then
            If (Me.Left + X + Me.Width - miX < Screen.Width) Then
                Me.Left = Me.Left + (X - miX)
            Else
                Me.Left = Screen.Width - Me.Width
            End If
        Else
            Me.Left = 0
        End If
       
        If (Me.Top + Y - miY > 0) Then
            If (Me.Top + Y + Me.Height - miY < Screen.Height) Then
                Me.Top = Me.Top + (Y - miY)
            Else
                Me.Top = Screen.Height - Me.Height
            End If
        Else
            Me.Top = 0
        End If
    End If
End Sub
0
 
woodsrrAuthor Commented:
Akkalam:  As I stated in the question, I dont want to use a time control and the way I'm currently doing it is better in my opinion.

DennisBorg:  I have used AddressOf for fast parsing, and graphics but never though to use it here and I'm not 100 percent clear what this does.  However I tried the code you included and got an error: Invalid Used of AddressOf and it highlights this line:
 OldWinProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf MyWinProc)

I started a new program, cut and pasted your code, moving the public const and declares to a Module.  Not sure what I'm doing wrong.  Going to go back and look at a VB Article on AddressOf and see if I can figure out what you are trying to tell me.
0
 
woodsrrAuthor Commented:
Sorry for the duplicate post..... Darn Refresh button...
0
 
woodsrrAuthor Commented:
Ok, I just reread.  The who code goes in a module. I no longer get the error.  But I doesnt keep the form on the screen.
0
 
woodsrrAuthor Commented:
OK, yes it does.  And it does crash VB.  I'll keep this in mind but will probably stick to the way I'm doing it now.  Thanks for your time all.
0
 
woodsrrAuthor Commented:
Answer was a B to C but no fault of DennisBorg, just the way VB is.  Effort was an 'A'.  Thanks.
0
 
DennisBorgCommented:
The AddressOf operator returns the Address Of the specified function; but this only works with functions that are in Standard Code Modules, *NOT* in any Object Code Modules (such as your form's code module).

In other words, you cannot have the MyWinProc() function in a Form Code Module, or in a Class Code Module, or anywhere, except in a Standard Code Module (.BAS)


You will want to close your form nomally (by clicking the 'X', or by your program Unloading the form), otherwise it will crash (as I've stated before, and as you've experienced personally)

The reason is that the Windows Operating System is going to call your Window Procedure (MyWinProc) to pass Window Messages to it. When you break your code so that you can trace line by line, Windows cannot invoke that function, hence it crashes.

Or if you click the "Stop" button on the VB menu bar, then your Form Unload event does not happen, which means your form does not get UNsubclassed. Again, Windows tries to call the Windows Procedure to your own function, which is no longer around, which causes a crash.

When you subclass a window, you are rerouting window messages, so that instead of them being sent to the Default Window Handler, they are sent to your own custom window handler (MyWinProc). Anytime you impede or prevent the ability of the Window Handler from processing messages sent to it by the Operating System (MS Windows), you are going to have problems.


I hope that makes sense.


-Dennis Borg
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.