Buat formulir tanpa bingkai bisa dipindahkan?

109

Apakah ada cara untuk membuat formulir yang tidak memiliki batas (FormBorderStyle disetel ke "none") dapat dipindahkan saat mouse diklik di formulir seperti jika ada batas?

pengguna
sumber

Jawaban:

252

Ini artikel tentang CodeProject Rincian teknik. Pada dasarnya intinya adalah:

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

Ini pada dasarnya melakukan hal yang sama persis dengan mengambil bilah judul jendela, dari sudut pandang pengelola jendela.

Joey
sumber
Ini sama sekali tidak berhasil untuk saya. Kode berjalan dengan baik, semuanya benar, dan jendela saya masih ada di sana. Ada ide?
dbrree
8
@dbrree Anda mungkin menyalin kode, yang tidak akan berfungsi karena Form1_MouseDowntidak ditetapkan ke MouseDownacara sebenarnya Form1.
Remon Ramy
2
Jika Anda memiliki label atau ikon (atau objek latar depan lainnya!) Di mana Anda mengambil formulir untuk diseret, tambahkan acara mousedown ke item ini juga. Peristiwa formulir tidak bisa melihat melalui objek formulir.
Paul Nelson
1
Anda perlu menambahkan this.MouseDown += ...ke Main()fungsi untuk formulir
Richard Peck
1
Bekerja seperti pesona bagi saya!!!! Saya harus memindahkan penanganan acara ke panel yang saya letakkan di formulir tetapi berhasil
Justin
54

Jangan membuat segalanya lebih sulit dari yang mereka butuhkan. Saya telah menemukan begitu banyak potongan kode yang memungkinkan Anda menyeret formulir (atau Kontrol lain). Dan banyak dari mereka memiliki kekurangan / efek sampingnya sendiri. Terutama yang saat mereka mengelabui Windows dengan berpikir bahwa Kontrol pada formulir adalah bentuk sebenarnya.

Karena itu, inilah cuplikan saya. Saya menggunakannya sepanjang waktu. Saya juga ingin mencatat bahwa Anda tidak boleh menggunakan this.Invalidate (); seperti yang suka dilakukan orang lain karena dalam beberapa kasus menyebabkan formulir berkedip. Dan dalam beberapa kasus, hal ini juga terjadi. Menggunakan this.Update, saya tidak mengalami masalah berkedip:

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;
    }
jay_t55
sumber
1
dang winforms ... mencari jawaban ini selama hampir dua jam .... im baru untuk c # meskipun, Anda membuat saya bertanya-tanya!
PrinceOfRavens
Ini persis seperti yang saya pikirkan, satu-satunya masalah saya adalah itu buggy sekali sampai saya membaca bagaimana Anda melakukannya. Terima kasih kawan
EasyBB
Yang ini bekerja lebih baik untuk saya daripada potongan kecil yang menakjubkan di atas WndProc yang menimpa. Pikiran Anda, WndProc berhasil ... itu hanya menghentikan hal-hal lain untuk bekerja. Terima kasih!
Mmm
Mungkin opsi terbaik di sini karena berfungsi dengan baik dan tidak bergantung pada WndProc (dapat dengan mudah ditransfer ke platform lain dengan Mono). Dapat ditingkatkan dengan mengubah status menjadi maksimal / normal jika Y <10
Sylverdrag
Ini tidak berfungsi pada mono, mengubah .net 4.5.2 menjadi mono / net 4.5 dan masih tidak berfungsi. Mencoba mencari solusi sekarang.
Roger Deep
35

Cara lain yang lebih sederhana untuk melakukan hal yang sama.

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;
}
elimad
sumber
1
Siapapun yang memungkinkan bahwa Anda dapat memindahkan formulir dengan memegang alat tertentu (misalnya label).
Thomas W
2
Jika diklik dua kali, formulir dimaksimalkan. Baca juga di sana-sini bahwa klik kanan ini putus.
Sebastiano
19

gunakan MouseDown, MouseMove dan MouseUp. Anda dapat mengatur bendera variabel untuk itu. Saya punya sampel, tapi menurut saya Anda perlu merevisi.

Saya mengkodekan tindakan mouse ke panel. Setelah Anda mengklik panel, formulir Anda akan ikut bergerak.

//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);     
  }
}
junmats
sumber
Ini. Seperti yang sudah dikatakan di tempat lain, ini bergantung pada bentuk yang masih menghasilkan peristiwa MouseMove. Sebagai kasus sederhana, misalkan Anda menilai Formulir di baris piksel paling atas dan menyeret ke atas. Tidak ada yang akan terjadi, meskipun formulir akan melompat-lompat segera setelah Anda menggerakkan mouse kembali ke atasnya.
Joey
12

WPF saja


tidak memiliki kode yang tepat untuk ditangani, tetapi dalam proyek baru-baru ini saya pikir saya menggunakan acara MouseDown dan sederhananya ini:

frmBorderless.DragMove();

Metode Window.DragMove (MSDN)

Chris
sumber
2
Itu WPF. Ok, OP tidak secara tepat menentukan ini.
Joey
Ya, yang merupakan sesuatu yang saya lupa tentang proyek yang saya lakukan. Saya baru saja melihat Formulir dan formulir itu tidak tersedia. Maaf!
Chris
3
@ Chris Ini berhasil untuk saya dalam proyek WPF. Terima kasih telah tidak menghapus jawabannya.
Rembunator
7

Ref. tautan video

Ini telah diuji dan mudah dimengerti.

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);
}
Gnana Akilan
sumber
6
Kode ini berfungsi, tetapi bagaimana itu mudah dimengerti? Saya tidak tahu tentang apa pun di segmen kode ini selain pernyataan sakelar!
Jamshaid Kamran
Ini WM_NCHITTESTmenyamar.
Andreas Rejbrand
4

Tidak ada properti yang dapat Anda balik untuk mewujudkannya secara ajaib. Lihatlah kejadian untuk formulir dan itu menjadi cukup sepele untuk menerapkan ini dengan menetapkan this.Topdan this.Left. Secara khusus, Anda ingin melihat MouseDown, MouseUpdan MouseMove.

Matthew Scharley
sumber
Saya pikir saya harus menggunakan acara-acara itu tetapi saya tidak yakin apa yang harus saya lakukan dengan mereka. Saat acara MouseDown dipanggil, bagaimana cara mengizinkan formulir dipindahkan?
pengguna
1
Pada mouse ke bawah Anda mengatur bendera dan menyimpan koordinat dasar. Saat gerakan mouse - jika bendera disetel - Anda menyesuaikan bagian atas dan kiri dengan offset koordinat mouse baru. Di mouse ke atas, Anda menghapus bendera.
Murph
Namun, Anda dapat melakukan ini dengan API Windows dengan cukup mudah yang tidak bergantung pada masih mendapatkan acara mouse. Metode ini gagal jika Anda mengambil piksel di tepi paling atas formulir dan menyeret ke atas, misalnya.
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;
  }
}

ini bisa menyelesaikan masalahmu ....

Syed Ali
sumber
Anda juga dapat menggunakan "e.Location" alih-alih Control.MousePosition
Reza Taibur
4

Cara terbaik yang pernah saya temukan (dimodifikasi tentu saja)

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

Untuk menerapkan seret ke kontrol cukup masukkan ini setelah InitializeComponent ()

AddDrag(NameOfControl);
Ricky Divjakovski
sumber
4

Itu berhasil untuk Aku.

    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);
        }
    }
Sudhananda Biswas
sumber
Selamat datang di Stack Overflow - tur , silakan periksa Bagaimana Menjawab . Anda harus memberikan penjelasan tentang bagaimana kode ini menyelesaikan masalah OP "poster asli". Akan lebih membantu bagi siapa saja yang memeriksa jawaban Anda.
Mauricio Arias Olave
2

Untuk .NET Framework 4,

Anda bisa menggunakan this.DragMove()untuk MouseDownacara komponen (mainLayout dalam contoh ini) yang Anda gunakan untuk menyeret.

private void mainLayout_MouseDown(object sender, MouseButtonEventArgs e)
{
    this.DragMove();
}
Neeraj Kumar Das
sumber
2

Cara termudah adalah:

Pertama buat label bernama label1. Buka acara label1> acara mouse> Label1_Mouse Pindah dan tulis ini:

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

Saya mencoba membuat bentuk jendela tanpa batas yang dapat dipindahkan yang berisi kontrol Host Elemen WPF dan kontrol Pengguna WPF.

Saya berakhir dengan panel tumpukan yang disebut StackPanel di kontrol pengguna WPF saya yang sepertinya hal logis untuk dicoba klik untuk bergerak. Mencoba kode junmats berhasil saat saya menggerakkan mouse perlahan, tetapi jika saya menggerakkan mouse lebih cepat, mouse akan keluar dari formulir dan formulir akan macet di suatu tempat di tengah gerakan.

Ini memperbaiki jawabannya untuk situasi saya menggunakan CaptureMouse dan ReleaseCaptureMouse dan sekarang mouse tidak bergerak dari formulir saat memindahkannya bahkan jika saya memindahkannya dengan cepat.

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);
Mike Sage
sumber
2

Karena beberapa jawaban tidak memungkinkan kontrol anak dapat diseret, saya telah membuat kelas pembantu kecil. Ini harus melewati formulir tingkat atas. Dapat dibuat lebih umum jika diinginkan.

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);
        }
    }
}
Sebastian
sumber
1

Selain itu, jika Anda perlu DoubleClick dan membuat Formulir Anda lebih besar / lebih kecil, Anda dapat menggunakan Jawaban pertama, buat variabel int global, tambahkan 1 setiap kali pengguna mengklik komponen yang Anda gunakan untuk menyeret. Jika variable == 2kemudian buat formulir Anda lebih besar / lebih kecil. Juga gunakan timer untuk setiap setengah detik atau satu detik untuk membuat variable = 0;

Billy Xd
sumber
1

Menambahkan MouseLeftButtonDownevent handler ke MainWindow berhasil untuk saya.

Dalam fungsi acara yang dibuat secara otomatis, tambahkan kode di bawah ini:

base.OnMouseLeftButtonDown(e);
this.DragMove();
Aparna Ratheesh
sumber
1

Saya memperluas solusi dari jay_t55 dengan satu metode lagi ToolStrip1_MouseLeaveyang menangani kejadian mouse bergerak cepat dan meninggalkan wilayah.

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;
}
Michael
sumber
0

Saya mencoba yang berikut dan presto changeo, jendela transparan saya tidak lagi membeku di tempatnya tetapi dapat dipindahkan !! (buang semua solusi kompleks lainnya di atas ...)

   private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        // Begin dragging the window
        this.DragMove();
    }
RoySeberg
sumber
Jawaban ini untuk WPF, pertanyaannya tentang WinForms.
Ray
0

Formulir1 (): new Moveable(control1, control2, control3);

Kelas:

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; }
                }
            };
        }
    }
}
Keajaiban Jiwa
sumber
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);
    }
Dragoslav Agbaba
sumber
Alangkah baiknya jika Anda mendukung jawaban Anda dengan beberapa penjelasan :)
Mustafamg