那裏的任何人都知道如何讓你的.net窗口形式的應用程序像Winamp一樣粘性/活潑,以便它捕捉到屏幕的邊緣?如何使我的Windows窗體應用程序捕捉到屏幕邊緣?
目標框架是使用VS08編寫的C#.NET 2.0 Windows窗體。我希望將此功能添加到自定義用戶控件中,但是我認爲更多的人會從應用程序及其主要形式描述中受益。
謝謝。
那裏的任何人都知道如何讓你的.net窗口形式的應用程序像Winamp一樣粘性/活潑,以便它捕捉到屏幕的邊緣?如何使我的Windows窗體應用程序捕捉到屏幕邊緣?
目標框架是使用VS08編寫的C#.NET 2.0 Windows窗體。我希望將此功能添加到自定義用戶控件中,但是我認爲更多的人會從應用程序及其主要形式描述中受益。
謝謝。
這個工作相當好,適用於多臺監視器,觀察任務欄:
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;
}
}
只是取回你的顯示器的當前像素的高度/寬度...
How to determine active monitor of the current cursor location
...和處理位置改變/移動的形式事件。當你進入時,比如25個像素左右的邊(主窗體的Location.Left +窗體寬度)或高度(主窗體的Location.Top +窗體高度),然後繼續設置.Left和.Top屬性以便您的應用程序「停靠」在角落裏。
編輯:另一個注意 - 當你真正做到了「噼噼啪啪」你可能還需要移動光標位置的相對距離,使其留在窗欄上的相同點。否則,當MouseMove和表單位置更改事件相互對抗時,表單可能會成爲光標位置和「活潑」功能之間的巨大乒乓球。
唯一的問題是一旦你停靠了,你永遠不會離開。確保允許鼠標移動(即點擊並拖動25個像素),一旦停靠,即可移除您的表單。 – 2009-02-26 06:03:44
如果這是您想要的功能,您可以將其稱爲「Docking Hotel California」。 – Brandon 2009-02-26 06:06:32
我不知道,如果你發現你的解決方案,但我創建了一個小部件的只是:http://www.formsnapper.net - 它卡跨越流程邊界!
接受的答案只在完成拖動後捕捉窗口,而我希望表單在拖動時連續捕捉到屏幕邊緣。這裏是我的解決方案,大致上以關閉Paint.NET source code:
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);
}
}
}
下面是什麼樣子:
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
請考慮到任務欄可以有不同屬性(頂部,底部,右側,左側,多行,不同的字體大小等)。您可能還需要考慮Windows Vista中的小工具欄。此外,您可能想要處理屏幕分辨率或任務欄大小的更改。 – 2009-02-27 17:01:05
要考慮的另一件事是允許用戶將窗口*移動到屏幕邊界之外。很多時候,我將一個窗口移動到屏幕邊界之外,只有一小部分可見。 – 2009-02-27 22:50:06