Results 1 to 2 of 2

Thread: Snapping windows to the screen and to other windows

  1. #1

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

    Snapping windows to the screen and to other windows

    Hey there,


    Here is a simple example on how to make windows snap both to the edges of any screen and to other windows. I think this is a feature that is quite common for some types of applications. The idea is that the user moves the window close enough to the edge of the screen or some other window, and the window will snap into place exactly besides the screen or other window.

    Note that this is just a simple example. You may want to add more functionality (or I may do it if I get some more time), such as:
    • Store which side the window is snapped to,
    • Also match the height or width of the snapped window,
    • (Difficult) Add something to avoid the small amount of flickering


    For now, this example will probably be a good base for whatever you need.


    First, let me explain the idea.

    Snapping to the screen(s)
    If the user moves the window, we constantly check the position in relation to the bounds of all the screens. We can get the bounds of these screens by iterating through the Screen.AllScreens property and using the WorkingArea property of each Screen object.
    vb.net Code:
    1. For Each screen As Screen In screen.AllScreens
    2.             Dim rect As Rectangle = screen.WorkingArea
    3.  
    4.         Next

    We have to decide whether the window is 'in range' to snap, and when it is, we actually snap it. This sounds harder than it actually is, so here is a graphical representation of the code:

    I have depicted the top of a screen with a window hovering slightly beneath it. Two dotted lines denote the 'snapping region', defined by a constant SNAP_SIZE parameter (in my code that is 25 pixels).

    Note that, in .NET, the y-axis points downward, so the y-coordinate of the top dotted line (above the screen) is Screen.Top - SNAP_SIZE. In the same way, the y-coordinate of the bottom dotted line is Screen.Top + SNAP_SIZE.

    If the window's y-coordinate is between these two values, it will be snapped to the top of the screen.

    But what does that mean exactly? When is the window between the two values? Well, take a look at the picture. The y-coordinate of the window (Form.Top property) should be below (= greater than!) the top dotted line, and above (= less than!) the bottom dotted line.

    If that is the case, we simply set the Form.Top property to the Top of the screen (snapping!).

    In code, that looks like this:
    vb.net Code:
    1. If Me.Top <= rect.Top + SNAP_SIZE AndAlso Me.Top > rect.Top - SNAP_SIZE Then
    2.                 Me.Top = rect.Top
    3.             End If

    Of course, the same goes for the other sides, with suitably modified code. If you apply this logic to all sides, the final code is:
    vb.net Code:
    1. Private Sub StickyWindow_Move(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Move
    2.         For Each screen As Screen In screen.AllScreens
    3.             Dim rect As Rectangle = screen.WorkingArea
    4.  
    5.             If Me.Left <= rect.Left + SNAP_SIZE AndAlso Me.Left > rect.Left - SNAP_SIZE Then
    6.                 Me.Left = rect.Left
    7.             ElseIf Me.Right >= rect.Right - SNAP_SIZE AndAlso Me.Right < rect.Right + SNAP_SIZE Then
    8.                 Me.Left = rect.Right - Me.Width
    9.             End If
    10.  
    11.             If Me.Top <= rect.Top + SNAP_SIZE AndAlso Me.Top > rect.Top - SNAP_SIZE Then
    12.                 Me.Top = rect.Top
    13.             ElseIf Me.Bottom >= rect.Bottom - SNAP_SIZE AndAlso Me.Bottom < rect.Bottom + SNAP_SIZE Then
    14.                 Me.Top = rect.Bottom - Me.Height
    15.             End If
    16.         Next
    17.     End Sub


    Snapping to other windows
    If you also want your windows to snap to other windows, you'll have to add another check in the same Move method. If you have just one window you want to snap to, you don't need another loop, but you just use the bounds of that window.

    In my example however, I decided that I wanted all of the StickyWindows to be able to stick to each other. For that to work, obviously, I needed to keep a list of all StickyWindows that have been created. Since each StickyWindow needs to use this list, it needs to be in a common place, accessible from anywhere. I decided to create a class called Common, with a Shared 'StickyWindows' property, of type List(Of StickyWindow). When a StickyWindow is created, I add it to the list, and once it is closed, I remove it again.

    Now, on to the actual snapping. Previously, we needed to snap to the inside of another rectangle (the screen's bounds). Now, we want to snap to the outside of each sticky window. It's only a slight difference, but an important one to keep in mind. I won't derive how I got the 'equations' in detail again, so here is the code:
    vb.net Code:
    1. For Each f As StickyWindow In Common.StickyWindows
    2.             If Me IsNot f Then
    3.                 Dim rect = f.Bounds
    4.  
    5.                 ' If the windows are exactly on top of each other, an infinite loop will occur
    6.                 ' (one form moving the other, moving the other, moving the other, etc),
    7.                 ' so we'll ignore that case, since there is no docking to be done anyway.
    8.                 If Me.Bounds = rect Then Exit For
    9.  
    10.  
    11.                 If Me.Right <= rect.Left + SNAP_SIZE AndAlso Me.Right > rect.Left - SNAP_SIZE Then
    12.                     Me.Left = rect.Left - Me.Width
    13.                 ElseIf Me.Left >= rect.Right - SNAP_SIZE AndAlso Me.Left < rect.Right + SNAP_SIZE Then
    14.                     Me.Left = rect.Right
    15.                 End If
    16.  
    17.                 If Me.Bottom <= rect.Top + SNAP_SIZE AndAlso Me.Bottom > rect.Top - SNAP_SIZE Then
    18.                     Me.Top = rect.Top - Me.Height
    19.                 ElseIf Me.Top >= rect.Bottom - SNAP_SIZE AndAlso Me.Top < rect.Bottom + SNAP_SIZE Then
    20.                     Me.Top = rect.Bottom
    21.                 End If
    22.             End If
    23.         Next
    There are two notes to be made:
    • We don't want to dock to ourselves, so we add a check for that (line 2),
    • We don't want to snap if the forms coincide exactly. That would result in form 1 snapping to form 2, which in turn snaps to form 1, which snaps to form 2, etc...



    Well, that's it. I've attached a sample project that includes the code here. Use it however you like
    Attached Files Attached Files

  2. #2

    Re: Snapping windows to the screen and to other windows

    This is pretty good stuff Nick. Good job.

Posting Permissions

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



Click Here to Expand Forum to Full Width