Click here to Skip to main content
12,504,695 members (56,774 online)
Click here to Skip to main content
Add your own
alternative version

Stats

66.3K views
1.9K downloads
48 bookmarked
Posted

A .NET Snap To Screen Form

, 24 Aug 2006 CPOL
Rate this:
Please Sign up or sign in to vote.
An article on snapping a Windows Form to a desktop screen border.

Sample Image - SnapTo.png

Introduction

This article demonstrates how to add a snap-to-screen feature to your application. This feature allows you to drag your window near the screen border and have the window automatically get pulled towards that border like a magnet. There is already a nice unmanaged C++ implementation here.

Background

At TrayGames, we have a client that delivers chat and games to your computer. Our client is written completely in .NET 2.0, using a combination of C# and VB.NET. We wanted our client to have the snap-to-screen functionality that other popular applications such as Winamp and Skype have.

Using the Code

To add snap-to-screen functionality to your application, you have to override the Windows.Forms.Form.WndProc method and handle the WM_WINDOWPOSCHANGING message. Your handler will call my custom SnapToDesktopBorder method:

Imports system.Runtime.InteropServices

Public Class Form1
    Private Const mSnapOffset As Integer = 35
    Private Const WM_WINDOWPOSCHANGING As Integer = &H46

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure WINDOWPOS
        Public hwnd As IntPtr
        Public hwndInsertAfter As IntPtr
        Public x As Integer
        Public y As Integer
        Public cx As Integer
        Public cy As Integer
        Public flags As Integer
    End Structure

    Protected Overrides Sub WndProc(ByRef m As Message)
        ' Listen for operating system messages
        Select Case m.Msg
            Case WM_WINDOWPOSCHANGING
                SnapToDesktopBorder(Me, m.LParam, 0)
        End Select

        MyBase.WndProc(m)
    End Sub

The heart of the functionality is in the implmentation of the SnapToDesktopBorder method. It calculates the distance to the desktop screen edges, and repositions the window as necessary:

    Public Shared Sub SnapToDesktopBorder(ByVal clientForm _
           As Form, ByVal LParam As IntPtr, ByVal widthAdjustment As Integer)
        If clientForm Is Nothing Then
            ' Satisfies rule: Validate parameters
            Throw New ArgumentNullException("clientForm")
        End If

        ' Snap client to the top, left, bottom or right desktop border
        ' as the form is moved near that border.

        Try
            ' Marshal the LPARAM value which is a WINDOWPOS struct
            Dim NewPosition As New WINDOWPOS
            NewPosition = CType(Runtime.InteropServices.Marshal.PtrToStructure( _
                LParam, GetType(WINDOWPOS)), WINDOWPOS)

            If NewPosition.y = 0 OrElse NewPosition.x = 0 Then
                Return ' Nothing to do!
            End If

            ' Adjust the client size for borders and caption bar
            Dim ClientRect As Rectangle = _
                clientForm.RectangleToScreen(clientForm.ClientRectangle)
            ClientRect.Width += _
                SystemInformation.FrameBorderSize.Width - widthAdjustment
            ClientRect.Height += (SystemInformation.FrameBorderSize.Height + _
                                  SystemInformation.CaptionHeight)

            ' Now get the screen working area (without taskbar)
            Dim WorkingRect As Rectangle = _
                Screen.GetWorkingArea(clientForm.ClientRectangle)

            ' Left border
            If NewPosition.x >= WorkingRect.X - mSnapOffset AndAlso _
                NewPosition.x <= WorkingRect.X + mSnapOffset Then
                NewPosition.x = WorkingRect.X
            End If

            ' Get screen bounds and taskbar height
            ' (when taskbar is horizontal)
            Dim ScreenRect As Rectangle = _
                Screen.GetBounds(Screen.PrimaryScreen.Bounds)
            Dim TaskbarHeight As Integer = _
                ScreenRect.Height - WorkingRect.Height

            ' Top border (check if taskbar is on top
            ' or bottom via WorkingRect.Y)
            If NewPosition.y >= -mSnapOffset AndAlso _
                 (WorkingRect.Y > 0 AndAlso NewPosition.y <= _
                 (TaskbarHeight + mSnapOffset)) OrElse _
                 (WorkingRect.Y <= 0 AndAlso NewPosition.y <= _
                 (mSnapOffset)) Then
                If TaskbarHeight > 0 Then
                    NewPosition.y = WorkingRect.Y ' Horizontal Taskbar
                Else
                    NewPosition.y = 0 ' Vertical Taskbar
                End If
            End If

            ' Right border
            If NewPosition.x + ClientRect.Width <= _
                 WorkingRect.Right + mSnapOffset AndAlso _
                 NewPosition.x + ClientRect.Width >= _
                 WorkingRect.Right - mSnapOffset Then
                NewPosition.x = WorkingRect.Right - (ClientRect.Width + _
                                SystemInformation.FrameBorderSize.Width)
            End If

            ' Bottom border
            If NewPosition.y + ClientRect.Height <= _
                   WorkingRect.Bottom + mSnapOffset AndAlso _
                   NewPosition.y + ClientRect.Height >= _
                   WorkingRect.Bottom - mSnapOffset Then
                NewPosition.y = WorkingRect.Bottom - (ClientRect.Height + _
                                SystemInformation.FrameBorderSize.Height)
            End If

            ' Marshal it back
            Runtime.InteropServices.Marshal.StructureToPtr(NewPosition, _
                                                           LParam, True)
        Catch ex As ArgumentException
        End Try
    End Sub
End Class    

You can adjust the mSnapOffset member variable to adjust the distance between your window and the screen border where the snap will happen. Note that the widthAdjustment parameter is available as a fudge factor in case your client has special width requirements. That's all there is to it!

Points of Interest

This article is interesting if you want to learn some creative ways to position Windows Forms. Although this wasn't the most complicated topic, hopefully, I've saved someone a little time figuring this out! If you are interested in creating your own multi-player online games, you should check out the TGSDK which is downloadable from the TrayGames web site.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Perry Marchant
Founder SpreadTrends.com
United States United States
I've authored many articles that tackle real-world issues to save my peers in the development community valuable time. For example I've written articles that: show how to decode Ogg Vorbis audio files using the .NET Framework; describe best practices for Improving Entity Framework performance; and demonstrate step-by-step how to create a multi-player game.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
GeneralMy vote of 4 Pin
3AgL3 DeeJay20-Aug-14 23:11
member3AgL3 DeeJay20-Aug-14 23:11 
QuestionProblem with big form with NoBorder style Pin
3AgL3 DeeJay20-Aug-14 23:08
member3AgL3 DeeJay20-Aug-14 23:08 
GeneralMy vote of 5 Pin
Jαved12-Jun-12 22:58
memberJαved12-Jun-12 22:58 
GeneralHow to snap to another form ? or instance of same form? Pin
Member 44231544-May-11 21:35
memberMember 44231544-May-11 21:35 
Generalif you'd like a c# version implementing optional forcing all of form to stay on screen Pin
BillWoodruff6-Aug-09 4:00
memberBillWoodruff6-Aug-09 4:00 
GeneralSimplier Pin
Ky Nam8-Nov-08 5:13
memberKy Nam8-Nov-08 5:13 
GeneralA MUCH EASIER WAY TO DO THAT.... Pin
Hamed.M16-Dec-07 6:34
memberHamed.M16-Dec-07 6:34 
QuestionHow would one change the mSnapoffset on the fly? Pin
danielj2323-Nov-07 3:51
memberdanielj2323-Nov-07 3:51 
AnswerRe: How would one change the mSnapoffset on the fly? Pin
danielj2323-Nov-07 3:53
memberdanielj2323-Nov-07 3:53 
JokeGreat code Pin
danielj2323-Nov-07 3:11
memberdanielj2323-Nov-07 3:11 
QuestionCould this be used to do docking? Pin
ToshRa8-Nov-07 11:29
memberToshRa8-Nov-07 11:29 
GeneralThanks Pin
pixeldoc200022-May-07 13:34
memberpixeldoc200022-May-07 13:34 
GeneralOh, snap!! Pin
mikeOA17-Oct-06 11:26
membermikeOA17-Oct-06 11:26 
GeneralRe: Oh, snap!! Pin
moneyshot29-Nov-06 9:44
membermoneyshot29-Nov-06 9:44 
GeneralRe: Oh, snap!! Pin
mikeOA29-Nov-06 14:40
membermikeOA29-Nov-06 14:40 
GeneralRe: Oh, snap!! Pin
moneyshot29-Nov-06 16:05
membermoneyshot29-Nov-06 16:05 
GeneralRe: Oh, snap!! Pin
mikeOA30-Nov-06 6:02
membermikeOA30-Nov-06 6:02 
GeneralRe: Oh, snap!! Pin
Cesa3724-Apr-08 3:20
memberCesa3724-Apr-08 3:20 
GeneralGood Work Pin
Polymorpher12-Sep-06 11:32
memberPolymorpher12-Sep-06 11:32 
GeneralNice, but doesn't work correctly with multiple desktops Pin
mav.northwind24-Aug-06 19:49
membermav.northwind24-Aug-06 19:49 
AnswerRe: Nice, but doesn't work correctly with multiple desktops [modified] Pin
Richard Rathmann29-Aug-06 12:39
memberRichard Rathmann29-Aug-06 12:39 
GeneralRe: Nice, but doesn't work correctly with multiple desktops Pin
Yep-ItsMe31-Aug-06 14:09
memberYep-ItsMe31-Aug-06 14:09 
GeneralFantastic stuff. [modified] Pin
nano_electronix24-Aug-06 12:48
membernano_electronix24-Aug-06 12:48 
GeneralVery cool. Pin
Marc Leger24-Aug-06 10:59
memberMarc Leger24-Aug-06 10:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160919.1 | Last Updated 24 Aug 2006
Article Copyright 2006 by Perry Marchant
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid