Tornar móvel uma forma sem borda?


109

Existe uma maneira de tornar um formulário sem borda (FormBorderStyle definido como "nenhum") móvel quando o mouse é clicado no formulário como se houvesse uma borda?

Respostas:


252

Este artigo em CodeProject detalha uma técnica. Basicamente se resume 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);
    }
}

Isso basicamente faz exatamente o mesmo que pegar a barra de título de uma janela, do ponto de vista do gerenciador de janelas.


Isso não funciona para mim de forma alguma. O código funciona bem, tudo está correto e minha janela ainda está parada lá. Alguma ideia?
dbrree

8
@dbrree Você provavelmente copiou o código, que não funcionará porque Form1_MouseDownnão está atribuído ao MouseDownevento real de Form1.
Remon Ramy

2
Se você tiver um rótulo ou um ícone (ou qualquer outro objeto de primeiro plano!) Onde está agarrando o formulário para arrastar, adicione um evento mousedown a esses itens também. Os eventos de formulário não podem ver através de objetos de formulário.
Paul Nelson de

1
Você precisa adicionar this.MouseDown += ...à Main()função para o formulário
Richard Peck

1
Caiu como uma luva para mim!!!! Tive de mover o tratamento de eventos para o painel que estava colocando no lugar do formulário, mas funcionou
Justin

54

Não vamos tornar as coisas mais difíceis do que precisam ser. Eu encontrei tantos trechos de código que permitem que você arraste um formulário (ou outro controle). E muitos deles têm suas próprias desvantagens / efeitos colaterais. Especialmente aqueles em que enganam o Windows fazendo-o pensar que um controle em um formulário é o formulário real.

Dito isso, aqui está o meu trecho. Eu uso isso o tempo todo. Também gostaria de observar que você não deve usar this.Invalidate (); como outros gostam de fazer porque faz com que o formulário pisque em alguns casos. E em alguns casos isso acontece. Usando this.Update, não tive problemas de oscilação:

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 ... procurei por esta resposta por quase duas horas .... sou novo em c # embora, você me fez maravilhas!
PrinceOfRavens

Este é exatamente o jeito que eu pensei, meu único problema era que havia muitos bugs até que eu li como você fazia. Obrigado amigo
EasyBB

Este funcionou melhor para mim do que o pequeno snippet incrível acima do WndProc. Lembre-se, o WndProc funcionou ... apenas impediu que outras coisas funcionassem. Obrigado!
Mmm

Provavelmente a melhor opção aqui, pois funciona bem e não depende do WndProc (pode ser facilmente transferido para outras plataformas com Mono). Pode ser melhorado alterando o estado para maximizado / normal se Y <10
Sylverdrag

Ele não funciona em mono, alterou .net 4.5.2 para mono / net 4.5 e ainda não funciona. Tentando encontrar uma solução agora.
Roger Deep

35

Outra maneira mais simples de fazer a mesma coisa.

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
Qualquer um que possibilite que você mova o formulário segurando uma ferramenta específica (por exemplo, um rótulo).
Thomas W

2
Se clicar duas vezes, o formulário será maximizado. Também leia aqui e ali que isso interrompe o clique com o botão direito.
Sebastiano

19

use MouseDown, MouseMove e MouseUp. Você pode definir um sinalizador de variável para isso. Eu tenho uma amostra, mas acho que você precisa revisar.

Estou codificando a ação do mouse em um painel. Depois de clicar no painel, seu formulário será movido com ele.

//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);     
  }
}

Isto é. Como já foi dito em outro lugar, isso depende do formulário ainda gerando eventos MouseMove. Como um caso simples, suponha que você gradue o Form na linha de pixels mais superior e arraste para cima. Nada acontecerá, embora o formulário salte assim que você mover o mouse de volta nele.
Joey de

12

WPF apenas


não tenho o código exato em mãos, mas em um projeto recente, acho que usei o evento MouseDown e simplesmente coloquei isto:

frmBorderless.DragMove();

Método Window.DragMove (MSDN)


2
Isso é WPF, no entanto. Ok, o OP não especificou exatamente isso.
Joey de

Sim, algo que esqueci do projeto que estava fazendo. Acabei de olhar o Formulários e ele não está disponível. Desculpe!
Chris

3
@Chris Isso funcionou para mim em um projeto WPF. Obrigado por não excluir a resposta.
Rembunator

7

Ref. link de vídeo

Isso é testado e 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, mas como é fácil de entender? Não sei de nada neste segmento de código além da instrução switch!
Jamshaid Kamran

Isso está WM_NCHITTESTdisfarçado.
Andreas Rejbrand 01 de

4

Não há propriedade que você possa alterar para fazer isso acontecer magicamente. Observe os eventos do formulário e torna-se bastante trivial implementar isso definindo this.Tope this.Left. Especificamente você vai querer olhar para MouseDown, MouseUpe MouseMove.


Achei que teria que usar esses eventos, mas não tenho certeza do que fazer com eles. Quando o evento MouseDown é chamado, como faço para permitir que o formulário seja movido?
usuário de

1
Com o mouse pressionado, você define uma bandeira e armazena as coordenadas da base. No movimento do mouse - se a bandeira estiver definida - você ajusta a parte superior e esquerda pelo deslocamento das novas coordenadas do mouse. Com o mouse para cima, você limpa a bandeira.
Murph de

Ainda assim, você pode fazer isso com a API do Windows bastante fácil, que não depende de ainda obter eventos do mouse. Este método falha se você pegar um pixel na borda superior do formulário e arrastar para cima, por exemplo.
Joey de

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

isso pode resolver seu problema ....


Você também pode usar "e.Location" em vez de Control.MousePosition
Reza Taibur


4

Melhor maneira que encontrei (modificada, é claro)

// 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 o arrasto a um controle, basta inseri-lo após InitializeComponent ()

AddDrag(NameOfControl);

4

Funcionou para mim.

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

Bem-vindo ao Stack Overflow - tour , consulte Como responder . Você deve fornecer uma explicação sobre como este código resolve o problema do "pôster original" do OP. Seria mais útil para quem verifica sua resposta.
Mauricio Arias Olave

2

Para .NET Framework 4,

Você pode usar this.DragMove()para o MouseDownevento do componente (mainLayout neste exemplo) que você está usando para arrastar.

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

2

A maneira mais fácil é:

Primeiro crie um rótulo denominado label1. Vá para eventos de label1> eventos de mouse> Label1_Mouse Move e escreva estes:

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

2

Eu estava tentando fazer um formulário de janelas sem borda móvel que continha um controle de host de elemento WPF e um controle de usuário WPF.

Acabei com um painel de pilha chamado StackPanel em meu controle de usuário WPF que parecia a coisa lógica para tentar clicar para mover. Tentar o código do junmats funcionou quando movi o mouse lentamente, mas se movesse o mouse mais rápido, o mouse se moveria para fora do formulário e o formulário ficaria preso em algum lugar no meio do movimento.

Isso melhorou sua resposta para a minha situação usando CaptureMouse e ReleaseCaptureMouse e agora o mouse não se move para fora do formulário enquanto o move, mesmo que eu o mova rapidamente.

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

Como algumas respostas não permitem que os controles filhos sejam arrastáveis, criei uma pequena classe auxiliar. Deve ser passado o formulário de nível superior. Pode ser mais genérico, se desejado.

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

Além disso, se precisar fazer o DoubleClick e tornar seu formulário maior / menor, você pode usar a primeira resposta, criar uma variável int global, adicionar 1 sempre que o usuário clicar no componente que você usa para arrastar. Em variable == 2seguida, torne seu formulário maior / menor. Use também um cronômetro a cada meio segundo ou segundo para fazer o seu variable = 0;


1

Adicionar um MouseLeftButtonDownmanipulador de eventos à MainWindow funcionou para mim.

Na função de evento que é gerada automaticamente, adicione o código abaixo:

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

1

Estou expandindo a solução de jay_t55 com mais um método ToolStrip1_MouseLeaveque trata o evento do mouse se movendo rapidamente e saindo da região.

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

Eu tentei o seguinte e pronto, minha janela transparente não estava mais congelada no lugar, mas podia ser movida !! (jogue fora todas as outras soluções complexas acima ...)

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

Esta resposta é para WPF, a pergunta é sobre WinForms.
Ray

0

Formulário 1(): new Moveable(control1, control2, control3);

Classe:

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á bom se você apoiar sua resposta com algumas explicações :)
Mustafamg
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.