¿Hacer móvil una forma sin bordes?


109

¿Hay alguna manera de hacer que un formulario que no tiene borde (FormBorderStyle se establece en "ninguno") se pueda mover cuando se hace clic con el mouse en el formulario como si hubiera un borde?

Respuestas:


252

Este artículo sobre CodeProject detalla una técnica. Básicamente se reduce a:

public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool ReleaseCapture();

private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{     
    if (e.Button == MouseButtons.Left)
    {
        ReleaseCapture();
        SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
    }
}

Básicamente, esto hace exactamente lo mismo que agarrar la barra de título de una ventana, desde el punto de vista del administrador de ventanas.


Esto no me funciona en absoluto. El código funciona bien, todo es correcto y mi ventana sigue ahí. ¿Algunas ideas?
dbrree

8
@dbrree Probablemente haya copiado el código, que no funcionará ya Form1_MouseDownque no está asignado al MouseDownevento real de Form1.
Remon Ramy

2
Si tiene una etiqueta o un icono (¡o cualquier otro objeto en primer plano!) Donde está tomando el formulario para arrastrarlo, agregue un evento de mousedown también a estos elementos. Los eventos de formulario no pueden ver a través de objetos de formulario.
Paul Nelson

1
Necesita agregar this.MouseDown += ...a la Main()función para el formulario
Richard Peck

1
Funcionó como un encanto para mí !!!! Tuve que mover el manejo de eventos al panel que estaba colocando en el lugar del formulario, pero funcionó
Justin

54

No hagamos las cosas más difíciles de lo necesario. Me he encontrado con tantos fragmentos de código que le permiten arrastrar un formulario (u otro Control). Y muchos de ellos tienen sus propios inconvenientes / efectos secundarios. Especialmente aquellos en los que engañan a Windows para que piense que un Control en un formulario es el formulario real.

Dicho esto, aquí está mi fragmento. Lo uso todo el tiempo. También me gustaría señalar que no debería usar this.Invalidate (); como a otros les gusta hacer porque hace que el formulario parpadee en algunos casos. Y en algunos casos también lo hace esto. Usando this.Update, no he tenido ningún problema de parpadeo:

private bool mouseDown;
private Point lastLocation;

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        mouseDown = true;
        lastLocation = e.Location;
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if(mouseDown)
        {
            this.Location = new Point(
                (this.Location.X - lastLocation.X) + e.X, (this.Location.Y - lastLocation.Y) + e.Y);

            this.Update();
        }
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        mouseDown = false;
    }

1
dang winforms ... busqué esta respuesta durante casi dos horas .... aunque soy nuevo en c #, ¡me hiciste maravillas!
PrinceOfRavens

Esta es exactamente la forma en que pensé, mi único problema era que tenía muchos errores hasta que leí cómo lo hiciste. Gracias amigo
EasyBB

Este funcionó mejor para mí que el pequeño fragmento asombroso sobre las anulaciones WndProc. Eso sí, el WndProc funcionó ... simplemente impidió que otras cosas funcionaran. ¡Gracias!
Mmm

Probablemente la mejor opción aquí, ya que funciona bien y no depende de WndProc (se puede migrar fácilmente a otras plataformas con Mono). Se puede mejorar cambiando el estado a maximizado / normal si Y <10
Sylverdrag

No funciona en mono, cambió .net 4.5.2 a mono / net 4.5 y todavía no funciona. Tratando de encontrar una solución ahora.
Roger Deep

35

Otra forma más sencilla de hacer lo mismo.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        // set this.FormBorderStyle to None here if needed
        // if set to none, make sure you have a way to close the form!
    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == WM_NCHITTEST)
            m.Result = (IntPtr)(HT_CAPTION);
    }

    private const int WM_NCHITTEST = 0x84;
    private const int HT_CLIENT = 0x1;
    private const int HT_CAPTION = 0x2;
}

1
Cualquiera de hacer posible que pueda mover el formulario sosteniendo una herramienta específica (por ejemplo, una etiqueta).
Thomas W

2
Si se hace doble clic, el formulario se maximiza. También lea aquí y allá que esto rompe el clic derecho.
Sebastiano

19

utilice MouseDown, MouseMove y MouseUp. Puede establecer una bandera de variable para eso. Tengo una muestra, pero creo que debes revisarla.

Estoy codificando la acción del mouse en un panel. Una vez que haga clic en el panel, su formulario se moverá con él.

//Global variables;
private bool _dragging = false;
private Point _offset;
private Point _start_point=new Point(0,0);


private void panel1_MouseDown(object sender, MouseEventArgs e)
{
   _dragging = true;  // _dragging is your variable flag
   _start_point = new Point(e.X, e.Y);
}

private void panel1_MouseUp(object sender, MouseEventArgs e)
{
   _dragging = false; 
}

private void panel1_MouseMove(object sender, MouseEventArgs e)
{
  if(_dragging)
  {
     Point p = PointToScreen(e.Location);
     Location = new Point(p.X - this._start_point.X,p.Y - this._start_point.Y);     
  }
}

Es. Como ya se dijo en otra parte, esto se basa en que el formulario aún genera eventos MouseMove. En un caso sencillo, suponga que gradúa el formulario en la fila de píxeles superior y lo arrastra hacia arriba. No sucederá nada, aunque el formulario saltará tan pronto como mueva el mouse hacia él.
Joey

12

Solo WPF


no tengo el código exacto a mano, pero en un proyecto reciente creo que usé el evento MouseDown y simplemente puse esto:

frmBorderless.DragMove();

Método Window.DragMove (MSDN)


2
Eso es WPF, sin embargo. Ok, el OP no especificó esto exactamente.
Joey

Sí, que es algo que me olvidé del proyecto que estaba haciendo. Solo miré Formularios y no está disponible. ¡Lo siento!
Chris

3
@Chris Esto funcionó para mí en un proyecto de WPF. Gracias por no borrar la respuesta.
Rembunator

7

Árbitro. enlace de video

Esto está probado y es fácil de entender.

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case 0x84:
            base.WndProc(ref m);
            if((int)m.Result == 0x1)
                m.Result = (IntPtr)0x2;
            return;
    }

    base.WndProc(ref m);
}

6
Este código funciona, pero ¿cómo es fácil de entender? ¡No sé nada en este segmento de código además de la declaración de cambio!
Jamshaid Kamran

Esto está WM_NCHITTESTdisfrazado.
Andreas Rejbrand

4

No hay propiedad que puedas voltear para que esto suceda por arte de magia. Mire los eventos del formulario y se vuelve bastante trivial implementar esto configurando this.Topy this.Left. Específicamente usted quiere mirar MouseDown, MouseUpy MouseMove.


Pensé que tendría que usar esos eventos, pero no estoy seguro de qué hacer con ellos. Cuando se llama al evento MouseDown, ¿cómo permito que se mueva el formulario?
usuario

1
Con el mouse hacia abajo, coloca una bandera y almacena las coordenadas de la base. Al mover el mouse, si la bandera está configurada, ajusta la parte superior e izquierda por el desplazamiento de las nuevas coordenadas del mouse. Con el mouse hacia arriba, borras la bandera.
Murph

Aún así, puede hacer esto con la API de Windows de manera bastante fácil, que no depende de seguir obteniendo eventos del mouse. Este método falla si agarra un píxel en el borde superior del formulario y lo arrastra hacia arriba, por ejemplo.
Joey

4
public Point mouseLocation;
private void frmInstallDevice_MouseDown(object sender, MouseEventArgs e)
{
  mouseLocation = new Point(-e.X, -e.Y);
}

private void frmInstallDevice_MouseMove(object sender, MouseEventArgs e)
{
  if (e.Button == MouseButtons.Left)
  {
    Point mousePos = Control.MousePosition;
    mousePos.Offset(mouseLocation.X, mouseLocation.Y);
    Location = mousePos;
  }
}

esto puede resolver tu problema ...


También puede usar "e.Location" en lugar de Control.MousePosition
Reza Taibur


4

La mejor forma que he encontrado (modificado por supuesto)

// This adds the event handler for the control
private void AddDrag(Control Control) { Control.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DragForm_MouseDown); }
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();

private void DragForm_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        ReleaseCapture();
        SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
        // Checks if Y = 0, if so maximize the form
        if (this.Location.Y == 0) { this.WindowState = FormWindowState.Maximized; }
    }
}

Para aplicar arrastre a un control, simplemente inserte esto después de InitializeComponent ()

AddDrag(NameOfControl);

4

Funcionó para mí.

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        _mouseLoc = e.Location;
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            int dx = e.Location.X - _mouseLoc.X;
            int dy = e.Location.Y - _mouseLoc.Y;
            this.Location = new Point(this.Location.X + dx, this.Location.Y + dy);
        }
    }

Bienvenido a Stack Overflow: recorrido , consulte Cómo responder . Debe proporcionar una explicación sobre cómo este código resuelve el problema del "póster original" de OP. Sería más útil para cualquiera que verifique su respuesta.
Mauricio Arias Olave

2

Para .NET Framework 4,

Puede usar this.DragMove()para el MouseDownevento del componente (mainLayout en este ejemplo) que está usando para arrastrar.

private void mainLayout_MouseDown(object sender, MouseButtonEventArgs e)
{
    this.DragMove();
}

2

La forma más sencilla es:

Primero cree una etiqueta llamada label1. Vaya a los eventos de label1> eventos del mouse> Label1_Mouse Move y escriba estos:

if (e.Button == MouseButtons.Left){
    Left += e.X;
    Top += e.Y;`
}

2

Estaba tratando de hacer que un formulario de ventanas sin bordes se pudiera mover y que contuviera un control WPF Element Host y un control de usuario WPF.

Terminé con un panel de pila llamado StackPanel en mi control de usuario de WPF, que parecía lo lógico para intentar hacer clic para mover. Probar el código de junmats funcionó cuando moví el mouse lentamente, pero si lo movía más rápido, el mouse se saldría del formulario y el formulario se atascaría en algún lugar a la mitad del movimiento.

Esto mejoró su respuesta para mi situación usando CaptureMouse y ReleaseCaptureMouse y ahora el mouse no se mueve fuera del formulario mientras lo muevo, incluso si lo muevo rápidamente.

private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
    _start_point = e.GetPosition(this);
    StackPanel.CaptureMouse();
}

private void StackPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
    StackPanel.ReleaseMouseCapture();
}

private void StackPanel_MouseMove(object sender, MouseEventArgs e)
{
    if (StackPanel.IsMouseCaptured)
    {
        var p = _form.GetMousePositionWindowsForms();
        _form.Location = new System.Drawing.Point((int)(p.X - this._start_point.X), (int)(p.Y - this._start_point.Y));
    }
}

    //Global variables;
    private Point _start_point = new Point(0, 0);

2

Dado que algunas respuestas no permiten que los controles secundarios se puedan arrastrar, he creado una pequeña clase de ayuda. Debe pasar el formulario de nivel superior. Puede hacerse más genérico si se desea.

class MouseDragger
{
    private readonly Form _form;
    private Point _mouseDown;

    protected void OnMouseDown(object sender, MouseEventArgs e)
    {
        _mouseDown = e.Location;
    }

    protected void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            int dx = e.Location.X - _mouseDown.X;
            int dy = e.Location.Y - _mouseDown.Y;
            _form.Location = new Point(_form.Location.X + dx, _form.Location.Y + dy);
        }
    }
    public MouseDragger(Form form)
    {
        _form = form;

        MakeDraggable(_form);            
    }

    private void MakeDraggable(Control control)
    {
        var type = control.GetType();
        if (typeof(Button).IsAssignableFrom(type))
        {
            return;
        }

        control.MouseDown += OnMouseDown;
        control.MouseMove += OnMouseMove;

        foreach (Control child in control.Controls)
        {
            MakeDraggable(child);
        }
    }
}

1

Además, si necesita hacer DoubleClick y hacer que su formulario sea más grande o más pequeño, puede usar la Primera respuesta, crear una variable int global, agregar 1 cada vez que el usuario haga clic en el componente que usa para arrastrar. Si, variable == 2entonces haz tu forma más grande / más pequeña. También use un temporizador por cada medio segundo o un segundo para hacer su variable = 0;


1

Agregar un MouseLeftButtonDowncontrolador de eventos a MainWindow funcionó para mí.

En el caso de que la función de evento se genere automáticamente, agregue el siguiente código:

base.OnMouseLeftButtonDown(e);
this.DragMove();

1

Estoy expandiendo la solución de jay_t55 con un método más ToolStrip1_MouseLeaveque maneja el evento de que el mouse se mueva rápidamente y abandone la región.

private bool mouseDown;
private Point lastLocation;

private void ToolStrip1_MouseDown(object sender, MouseEventArgs e) {
    mouseDown = true;
    lastLocation = e.Location;
}

private void ToolStrip1_MouseMove(object sender, MouseEventArgs e) {
    if (mouseDown) {
        this.Location = new Point(
            (this.Location.X - lastLocation.X) + e.X, (this.Location.Y - lastLocation.Y) + e.Y);

        this.Update();
    }
}

private void ToolStrip1_MouseUp(object sender, MouseEventArgs e) {
    mouseDown = false;
}

private void ToolStrip1_MouseLeave(object sender, EventArgs e) {
    mouseDown = false;
}

0

Probé lo siguiente y listo, ¡mi ventana transparente ya no estaba congelada en su lugar, pero se podía mover! (deseche todas esas otras soluciones complejas anteriores ...)

   private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        // Begin dragging the window
        this.DragMove();
    }

Esta respuesta es para WPF, la pregunta es sobre WinForms.
Ray

0

Formulario 1(): new Moveable(control1, control2, control3);

Clase:

using System;
using System.Windows.Forms;

class Moveable
{
    public const int WM_NCLBUTTONDOWN = 0xA1;
    public const int HT_CAPTION = 0x2;
    [System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
    [System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
    public static extern bool ReleaseCapture();
    public Moveable(params Control[] controls)
    {
        foreach (var ctrl in controls)
        {
            ctrl.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture();
                    SendMessage(ctrl.FindForm().Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
                    // Checks if Y = 0, if so maximize the form
                    if (ctrl.FindForm().Location.Y == 0) { ctrl.FindForm().WindowState = FormWindowState.Maximized; }
                }
            };
        }
    }
}

0
   [DllImport("user32.DLL", EntryPoint = "ReleaseCapture")]
    private extern static void ReleaseCapture();

    [DllImport("user32.DLL", EntryPoint = "SendMessage")]
    private extern static void SendMessage(System.IntPtr hWnd, int Msg, int wParam, int lParam);
    private void panelTitleBar_MouseDown(object sender, MouseEventArgs e)
    {
        ReleaseCapture();
        SendMessage(this.Handle, 0x112, 0xf012, 0);
    }

Sería bueno si apoya su respuesta con algunas explicaciones :)
Mustafamg
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.