A .NET Snap To Screen Form
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 theWindows.Forms.Form.WndProc
method and handle the WM_WINDOWPOSCHANGING
message. Your handler will call my custom SnapToDesktopBorder
method:
Hide Copy Code
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:
Hide Shrink Copy Code
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!
0 Commentaires