如何使我的Windows窗体应用程序捕捉到屏幕边缘?

那里的任何人都知道如何让你的.net窗口forms应用程序像Winamp粘滞/活泼,所以它捕捉到屏幕的边缘?

目标框架是用C#编写的.NET 2.0 Windows Form,使用VS08。 我正在寻找将这个function添加到自定义的用户控件,但我想更多的人会受益于它的应用程序和它的主要forms描述。

谢谢。

这工作得很好,在多个显示器上工作,观察任务栏:

public partial class Form1 : Form { public Form1() { InitializeComponent(); } private const int SnapDist = 100; private bool DoSnap(int pos, int edge) { int delta = pos - edge; return delta > 0 && delta <= SnapDist; } protected override void OnResizeEnd(EventArgs e) { base.OnResizeEnd(e); Screen scn = Screen.FromPoint(this.Location); if (DoSnap(this.Left, scn.WorkingArea.Left)) this.Left= scn.WorkingArea.Left; if (DoSnap(this.Top, scn.WorkingArea.Top)) this.Top = scn.WorkingArea.Top; if (DoSnap(scn.WorkingArea.Right, this.Right)) this.Left = scn.WorkingArea.Right - this.Width; if (DoSnap(scn.WorkingArea.Bottom, this.Bottom)) this.Top = scn.WorkingArea.Bottom - this.Height; } } 

接受的答案只是在完成拖动后捕捉窗口,而我希望窗体在拖动的同时连续捕捉到屏幕边缘。 这里是我的解决方案,松散地基于Paint.NET源代码 :

 using System; using System.ComponentModel; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; namespace Whatever { /// <summary> /// Managed equivalent of the Win32 <code>RECT</code> structure. /// </summary> [StructLayout(LayoutKind.Sequential)] public struct LtrbRectangle { public int Left; public int Top; public int Right; public int Bottom; public LtrbRectangle(int left, int top, int right, int bottom) { Left = left; Top = top; Right = right; Bottom = bottom; } public Rectangle ToRectangle() { return Rectangle.FromLTRB(Left, Top, Right, Bottom); } public static LtrbRectangle FromRectangle(Rectangle rect) { return new LtrbRectangle(rect.X, rect.Y, rect.X + rect.Width, rect.Y + rect.Height); } public override string ToString() { return "{Left=" + Left + ",Top=" + Top + ",Right=" + Right + ",Bottom=" + Bottom + "}"; } } /// <summary> /// A form that "snaps" to screen edges when moving. /// </summary> public class AnchoredForm : Form { private const int WmEnterSizeMove = 0x0231; private const int WmMoving = 0x0216; private const int WmSize = 0x0005; private SnapLocation _snapAnchor; private int _dragOffsetX; private int _dragOffsetY; /// <summary> /// Flags specifying which edges to anchor the form at. /// </summary> [Flags] public enum SnapLocation { None = 0, Left = 1 << 0, Top = 1 << 1, Right = 1 << 2, Bottom = 1 << 3, All = Left | Top | Right | Bottom } /// <summary> /// How far from the screen edge to anchor the form. /// </summary> [Browsable(true)] [DefaultValue(10)] [Description("The distance from the screen edge to anchor the form.")] public virtual int AnchorDistance { get; set; } = 10; /// <summary> /// Gets or sets how close the form must be to the /// anchor point to snap to it. A higher value gives /// a more noticable "snap" effect. /// </summary> [Browsable(true)] [DefaultValue(20)] [Description("The maximum form snapping distance.")] public virtual int SnapDistance { get; set; } = 20; /// <summary> /// Re-snaps the control to its current anchor points. /// This can be useful for re-positioning the form after /// the screen resolution changes. /// </summary> public void ReSnap() { SnapTo(_snapAnchor); } /// <summary> /// Forces the control to snap to the specified edges. /// </summary> /// <param name="anchor">The screen edges to snap to.</param> public void SnapTo(SnapLocation anchor) { Screen currentScreen = Screen.FromPoint(Location); Rectangle workingArea = currentScreen.WorkingArea; if ((anchor & SnapLocation.Left) != 0) { Left = workingArea.Left + AnchorDistance; } else if ((anchor & SnapLocation.Right) != 0) { Left = workingArea.Right - AnchorDistance - Width; } if ((anchor & SnapLocation.Top) != 0) { Top = workingArea.Top + AnchorDistance; } else if ((anchor & SnapLocation.Bottom) != 0) { Top = workingArea.Bottom - AnchorDistance - Height; } _snapAnchor = anchor; } private bool InSnapRange(int a, int b) { return Math.Abs(a - b) < SnapDistance; } private SnapLocation FindSnap(ref Rectangle effectiveBounds) { Screen currentScreen = Screen.FromPoint(effectiveBounds.Location); Rectangle workingArea = currentScreen.WorkingArea; SnapLocation anchor = SnapLocation.None; if (InSnapRange(effectiveBounds.Left, workingArea.Left + AnchorDistance)) { effectiveBounds.X = workingArea.Left + AnchorDistance; anchor |= SnapLocation.Left; } else if (InSnapRange(effectiveBounds.Right, workingArea.Right - AnchorDistance)) { effectiveBounds.X = workingArea.Right - AnchorDistance - effectiveBounds.Width; anchor |= SnapLocation.Right; } if (InSnapRange(effectiveBounds.Top, workingArea.Top + AnchorDistance)) { effectiveBounds.Y = workingArea.Top + AnchorDistance; anchor |= SnapLocation.Top; } else if (InSnapRange(effectiveBounds.Bottom, workingArea.Bottom - AnchorDistance)) { effectiveBounds.Y = workingArea.Bottom - AnchorDistance - effectiveBounds.Height; anchor |= SnapLocation.Bottom; } return anchor; } protected override void WndProc(ref Message m) { switch (m.Msg) { case WmEnterSizeMove: case WmSize: // Need to handle window size changed as well when // un-maximizing the form by dragging the title bar. _dragOffsetX = Cursor.Position.X - Left; _dragOffsetY = Cursor.Position.Y - Top; break; case WmMoving: LtrbRectangle boundsLtrb = Marshal.PtrToStructure<LtrbRectangle>(m.LParam); Rectangle bounds = boundsLtrb.ToRectangle(); // This is where the window _would_ be located if snapping // had not occurred. This prevents the cursor from sliding // off the title bar if the snap distance is too large. Rectangle effectiveBounds = new Rectangle( Cursor.Position.X - _dragOffsetX, Cursor.Position.Y - _dragOffsetY, bounds.Width, bounds.Height); _snapAnchor = FindSnap(ref effectiveBounds); LtrbRectangle newLtrb = LtrbRectangle.FromRectangle(effectiveBounds); Marshal.StructureToPtr(newLtrb, m.LParam, false); m.Result = new IntPtr(1); break; } base.WndProc(ref m); } } } 

以下是它的样子:

截图

只需要检索当前显示器的像素高度/宽度…

如何确定当前光标位置的活动监视器

…并处理表单的位置已更改/移动的事件。 当你进入内部时,比如25个像素左右的边(主表单的Location.Left +表单宽度)或者高度(主窗体的Location.Top +表单高度),然后继续设置.Left和.Top属性以便您的应用程序“停靠”在角落里。

编辑:另外一个注意事项 – 当你真正做到“贴紧”的时候,你也可能想移动光标位置的相对距离,使它保持在窗口栏的同一个点上。 否则,当MouseMove和表单位置更改事件相互对抗时,表单可能会成为光标位置和“活泼”功能之间的巨大乒乓球。

我不知道你是否找到了你的解决方案,但是我创建了一个小组件: http : //www.formsnapper.net – 它跨越了进程的边界。

https://github.com/stax76/staxrip

 Protected Overrides Sub WndProc(ByRef m As Message) Snap(m) MyBase.WndProc(m) End Sub Private IsResizing As Boolean Sub Snap(ByRef m As Message) Select Case m.Msg Case &H214 'WM_SIZING IsResizing = True Case &H232 'WM_EXITSIZEMOVE IsResizing = False Case &H46 'WM_WINDOWPOSCHANGING If Not IsResizing Then Snap(m.LParam) End Select End Sub Sub Snap(handle As IntPtr) Dim workingArea = Screen.FromControl(Me).WorkingArea Dim newPos = DirectCast(Marshal.PtrToStructure(handle, GetType(WindowPos)), WindowPos) Dim snapMargin = Control.DefaultFont.Height Dim border As Integer If OSVersion.Current >= OSVersion.Windows8 Then border = (Width - ClientSize.Width) \ 2 - 1 If newPos.Y <> 0 Then If Math.Abs(newPos.Y - workingArea.Y) < snapMargin AndAlso Top > newPos.Y Then newPos.Y = workingArea.Y ElseIf Math.Abs(newPos.Y + Height - (workingArea.Bottom + border)) < snapMargin AndAlso Top < newPos.Y Then newPos.Y = (workingArea.Bottom + border) - Height End If End If If newPos.X <> 0 Then If Math.Abs(newPos.X - (workingArea.X - border)) < snapMargin AndAlso Left > newPos.X Then newPos.X = workingArea.X - border ElseIf Math.Abs(newPos.X + Width - (workingArea.Right + border)) < snapMargin AndAlso Left < newPos.X Then newPos.X = (workingArea.Right + border) - Width End If End If Marshal.StructureToPtr(newPos, handle, True) End Sub