2009-02-26 10 views
22

Chiunque là fuori sa come rendere la vostra .net windows form app appiccicosa/scattante come Winamp in modo che si agganci ai bordi dello schermo?Come rendere la mia app Windows Form snap ai bordi dello schermo?

Il framework di destinazione sarebbe .NET 2.0 Windows Form scritto in C#, utilizzando VS08. Sto cercando di aggiungere questa funzionalità a un controllo utente personalizzato, ma ho pensato che più persone avrebbero beneficiato di averlo descritto per l'applicazione e la sua forma principale.

Grazie.

+0

Si prega di tenere conto che la barra delle applicazioni possono avere diverse proprietà (in alto, in basso, a destra, a sinistra, a più file, a diverse dimensioni del carattere, ecc.). Potresti anche voler rendere conto della barra dei gadget in Windows Vista. Inoltre, è possibile gestire le modifiche alla risoluzione dello schermo o alle dimensioni della barra delle applicazioni. –

+0

Un'altra cosa da considerare è consentire all'utente di spostare la finestra * all'esterno * dei bordi dello schermo. Molte volte sposto una finestra fuori dai bordi dello schermo per avere solo una piccola parte visibile. –

risposta

38

questo ha funzionato abbastanza bene, lavora su più monitor, osserva la barra delle applicazioni:

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

Questo cambia anche l'area di lavoro dello schermo in modo che lo spazio massimo utilizzabile sul desktop sia ridotto (ad esempio, le finestre ingrandite non andrebbero in primo piano)? –

+0

funziona proprio come un fascino!:) e Se si desidera limitare l'utente a posizionare il modulo tutto sullo schermo (per non superare l'area di lavoro) basta modificare questa riga: 'return delta> 0 && delta <= SnapDist;' a: 'return (delta <0) || (delta> 0 && delta <= SnapDist); ' – faza

2

Basta recuperare la corrente di pixel in altezza/larghezza del monitor si è in ...

How to determine active monitor of the current cursor location

... ed elaborare la posizione cambiato/eventi per il modulo mosso. Quando ci si avvicina, diciamo a 25 pixel circa di un bordo (altezza della forma della struttura principale, sinistra + larghezza della forma) o altezza (posizione della forma principale.Top + altezza della forma), quindi vai avanti e imposta le proprietà .Left e .Top in modo che la tua applicazione "attracca" negli angoli.

Modifica: Un'altra nota: quando si esegue effettivamente lo "snap", è anche possibile spostare la posizione del cursore sulla distanza relativa per mantenerla sullo stesso punto sulla barra della finestra. Altrimenti, la tua forma potrebbe diventare una gigantesca palla da ping pong tra la posizione del cursore e la tua funzionalità "scattante" mentre MouseMove e gli eventi modificati dalla posizione della forma si combattono l'uno contro l'altro.

+0

l'unico problema è che una volta che sei ancorato non puoi mai andartene. Assicurati di consentire il movimento del mouse (ad esempio, fai clic e trascina 25 pixel) una volta ancorato che può sganciare il modulo. –

+2

Se questa fosse la funzionalità desiderata, potremmo chiamarla "Docking Hotel California". – Brandon

+1

Non pensi di dover consentire la barra delle applicazioni e altre barre degli strumenti invece di oscurarle? – MarkJ

0

Non so se avete trovato la soluzione, ma ho creato un piccolo componente proprio per quello: http://www.formsnapper.net - non scatta attraverso i confini del processo!

6

La risposta accettata scatta solo la finestra dopo aver terminato il trascinamento, mentre volevo che il modulo continuasse a scattare sui bordi dello schermo durante il trascinamento. Ecco la mia soluzione, liberamente ispirato al largo della 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); 
     } 
    } 
} 

Ed ecco come appare:

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 
+0

Non sarebbe una cattiva idea fornire anche dettagli, commenti, spiegazioni, ecc. non solo codice, SO gli utenti ne trarranno maggior beneficio in futuro. Solo un pensiero :) –

+0

Non sono bravo a scrivere commenti, qualcuno ha inviato il codice come richiesta pull a staxrip e l'ho reso molto più breve rimuovendo tutti i commenti e altre cose, dopo che i creatori aggiornavano era rotto così l'ho riparato, le persone possono usa un recente test staxrip per testarlo. La richiesta pull originale è qui: https://github.com/stax76/staxrip/pull/5 – stax76

Problemi correlati