2009-02-26 45 views
22

那裏的任何人都知道如何讓你的.net窗口形式的應用程序像Winamp一樣粘性/活潑,以便它捕捉到屏幕的邊緣?如何使我的Windows窗體應用程序捕捉到屏幕邊緣?

目標框架是使用VS08編寫的C#.NET 2.0 Windows窗體。我希望將此功能添加到自定義用戶控件中,但是我認爲更多的人會從應用程序及其主要形式描述中受益。

謝謝。

+0

請考慮到任務欄可以有不同屬性(頂部,底部,右側,左側,多行,不同的字體大小等)。您可能還需要考慮Windows Vista中的小工具欄。此外,您可能想要處理屏幕分辨率或任務欄大小的更改。 – 2009-02-27 17:01:05

+0

要考慮的另一件事是允許用戶將窗口*移動到屏幕邊界之外。很多時候,我將一個窗口移動到屏幕邊界之外,只有一小部分可見。 – 2009-02-27 22:50:06

回答

38

這個工作相當好,適用於多臺監視器,觀察任務欄:

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; 
    } 
    } 
2

只是取回你的顯示器的當前像素的高度/寬度...

How to determine active monitor of the current cursor location

...和處理位置改變/移動的形式事件。當你進入時,比如25個像素左右的邊(主窗體的Location.Left +窗體寬度)或高度(主窗體的Location.Top +窗體高度),然後繼續設置.Left和.Top屬性以便您的應用程序「停靠」在角落裏。

編輯:另一個注意 - 當你真正做到了「噼噼啪啪」你可能還需要移動光標位置的相對距離,使其留在窗欄上的相同點。否則,當MouseMove和表單位置更改事件相互對抗時,表單可能會成爲光標位置和「活潑」功能之間的巨大乒乓球。

+0

唯一的問題是一旦你停靠了,你永遠不會離開。確保允許鼠標移動(即點擊並拖動25個像素),一旦停靠,即可移除您的表單。 – 2009-02-26 06:03:44

+2

如果這是您想要的功能,您可以將其稱爲「Docking Hotel California」。 – Brandon 2009-02-26 06:06:32

0

我不知道,如果你發現你的解決方案,但我創建了一個小部件的只是:http://www.formsnapper.net - 它卡跨越流程邊界!

6

接受的答案只在完成拖動後捕捉窗口,而我希望表單在拖動時連續捕捉到屏幕邊緣。這裏是我的解決方案,大致上以關閉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); 
     } 
    } 
} 

下面是什麼樣子:

Screenshot

0

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 
相關問題