2009-02-26 14 views
22

¿Alguien por ahí sabe cómo hacer que su aplicación de Windows .net sea pegajosa/rápida como Winamp, por lo que se ajusta a los bordes de la pantalla?¿Cómo hacer que mi aplicación Windows Form se ajuste a los bordes de la pantalla?

El marco de destino sería NET 2.0 de Windows Forms escrito en C#, usando VS08. Estoy buscando agregar esta funcionalidad a un control de usuario personalizado, pero calculé que más personas se beneficiarían de tenerla descrita para la aplicación y su formulario principal.

Gracias.

+0

Por favor tener en cuenta que la barra de tareas puede tener diferentes propiedades (arriba, abajo, derecha, izquierda, múltiples filas, diferente tamaño de letra, etc.). Es posible que también desee contabilizar la barra de gadgets en Windows Vista. Además, es posible que desee controlar los cambios en la resolución de pantalla o el tamaño de la barra de tareas. –

+0

Otra cosa a tener en cuenta es permitir al usuario mover la ventana * fuera * de los bordes de la pantalla. Muchas veces muevo una ventana fuera de los bordes de la pantalla para tener solo una pequeña parte visible. –

Respuesta

38

Esto funcionó bastante bien, trabaja en varios monitores, observa la barra de tareas:

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

¿Esto también cambia el área de trabajo de las pantallas, de modo que se reduce el espacio máximo que se puede utilizar en el escritorio (es decir, las ventanas maximizadas no deberían estar en la parte superior)? –

+0

funciona como un encanto!:) y si quiere limitar al usuario a colocar el formulario en la pantalla (para no exceder el área de trabajo) simplemente cambie esta línea: 'return delta> 0 && delta <= SnapDist;' to: 'return (delta <0) || (delta> 0 && delta <= SnapDist); ' – faza

2

Sólo recuperar la corriente de píxeles de altura/anchura del monitor que está en ...

How to determine active monitor of the current cursor location

... y procesar la ubicación cambió/movido eventos para el formulario. Cuando te encuentres dentro, digamos 25 píxeles más o menos de un borde (la forma de tu formulario principal. Izquierda + ancho del formulario) o la altura (la forma de tu formulario principal.Top + altura del formulario), luego sigue y configura las propiedades .Left y .Top para que su aplicación "atraque" en las esquinas.

Editar: Otra nota: cuando realmente hace el "ajuste", también puede mover la posición del cursor la distancia relativa para que permanezca en el mismo punto en la barra de la ventana. De lo contrario, su forma puede convertirse en una pelota de ping pong gigante entre la posición del cursor y su funcionalidad "rápida", ya que el MouseMove y la ubicación de la forma cambian los eventos que luchan entre sí.

+0

el único problema con eso es que una vez que esté conectado, nunca podrá irse. Asegúrese de permitir un movimiento del mouse (es decir, hacer clic y arrastrar 25 píxeles) una vez acoplado que puede desacoplar su formulario. –

+2

Si esa fuera la funcionalidad deseada, podría llamarla "Docking Hotel California". – Brandon

+1

¿No cree que debería permitir la barra de tareas y otras barras de herramientas en lugar de oscurecerlas? – MarkJ

0

No sé si encontró su solución, pero he creado un pequeño componente de eso: http://www.formsnapper.net - que encaje a través de los límites del proceso!

6

La respuesta aceptada solamente se asiente la ventana después de terminar el arrastre, mientras que quería la forma para ajustar de forma continua a los bordes de la pantalla mientras se arrastra. Aquí está mi solución, basada libremente fuera de la 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); 
     } 
    } 
} 

Y esto es lo que parece:

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

No sería una mala idea dar también algunos detalles, comentarios, explicaciones, etc., no solo el código, sino que los usuarios se beneficiarán más en el futuro. Solo un pensamiento :) –

+0

No soy bueno escribiendo comentarios, alguien envió el código como solicitud de extracción a staxrip y lo hice mucho más corto eliminando todos los comentarios y otras cosas, después de que los creadores actualizaran, estaba roto así que lo solucioné, la gente puede use una compilación de prueba staxrip reciente para probarlo. La solicitud de extracción original está aquí: https://github.com/stax76/staxrip/pull/5 – stax76

Cuestiones relacionadas