C# 如何使我的 Windows 窗体应用程序对齐屏幕边缘?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/589268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make my Windows Form app snap to screen edges?
提问by erator
Anyone out there know how to make your .net windows form app sticky/snappy like Winamp so it snaps to the edges of the screen?
任何人都知道如何让您的 .net 窗口形成像 Winamp 一样的粘性/活泼的应用程序,以便它捕捉到屏幕的边缘?
The target framework would be .NET 2.0 Windows Form written in C#, using VS08. I am looking to add this functionality to a custom user control, but I figured more people would benefit from having it described for the application and its main form.
目标框架将是使用 VS08 用 C# 编写的 .NET 2.0 Windows 窗体。我希望将此功能添加到自定义用户控件中,但我认为更多的人会从为应用程序及其主窗体描述它中受益。
Thank you.
谢谢你。
采纳答案by Hans Passant
This worked pretty well, works on multiple monitors, observes the taskbar:
这工作得很好,适用于多个显示器,观察任务栏:
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;
}
}
回答by Brandon
Just retrieve the current pixel height/width of the monitor you're on...
只需检索您所在显示器的当前像素高度/宽度...
How to determine active monitor of the current cursor location
... and process the location changed/moved events for the form. When you get within, say 25 pixels or so of an edge (your main form's Location.Left + form width) or height (your main form's Location.Top + form height), then go ahead and set the .Left and .Top properties so that your application "docks" in the corners.
...并处理表单的位置更改/移动事件。当你进入时,比如边缘(你的主窗体的 Location.Left + 窗体宽度)或高度(你的主窗体的 Location.Top + 窗体高度)的 25 像素左右,然后继续设置 .Left 和 .Top 属性以便您的应用程序“停靠”在角落。
Edit:One other note - when you actually do the "snapping" you may also want to move the cursor position the relative distance to make it stay on the same point on the window bar. Otherwise your form may become a giant ping pong ball between the cursor position and your "snappy" functionality as the MouseMove and form location changed events fight against each other.
编辑:另一个注意事项 - 当您实际执行“捕捉”时,您可能还希望将光标位置移动相对距离,使其停留在窗口栏上的同一点上。否则,您的表单可能会在光标位置和您的“活泼”功能之间变成一个巨大的乒乓球,因为 MouseMove 和表单位置更改事件相互对抗。
回答by Daniel Mo?mondor
I don't know if you found your solution, but I created a small component for just that: http://www.formsnapper.net- it snaps accross the process boundaries!
我不知道您是否找到了您的解决方案,但我为此创建了一个小组件:http: //www.formsnapper.net- 它跨越进程边界!
回答by Andrew Sun
The accepted answer only snaps the window after finishing the drag, whereas I wanted the form to continuously snap to the screen edges while dragging. Here's my solution, loosely based off the Paint.NET source code:
接受的答案仅在完成拖动后捕捉窗口,而我希望表单在拖动时连续捕捉到屏幕边缘。这是我的解决方案,大致基于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);
}
}
}
And here's what it looks like:
这是它的样子:
回答by stax76
https://github.com/stax76/staxrip
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