ทำให้แบบฟอร์มไร้ขอบสามารถเคลื่อนย้ายได้หรือไม่?


109

มีวิธีสร้างแบบฟอร์มที่ไม่มีเส้นขอบหรือไม่ (FormBorderStyle ตั้งค่าเป็น "none") สามารถเคลื่อนย้ายได้เมื่อคลิกเมาส์ลงบนแบบฟอร์มเหมือนกับว่ามีเส้นขอบหรือไม่?

คำตอบ:


252

นี้บทความเกี่ยวกับรายละเอียด CodeProject เทคนิค โดยทั่วไปเดือดลงไปที่:

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

โดยพื้นฐานแล้วสิ่งนี้จะเหมือนกับการจับแถบหัวเรื่องของหน้าต่างจากมุมมองของตัวจัดการหน้าต่าง


สิ่งนี้ไม่ได้ผลสำหรับฉันเลย รหัสทำงานได้ดีทุกอย่างถูกต้องและหน้าต่างของฉันยังคงนั่งอยู่ที่นั่น ความคิดใด ๆ ?
dbrree

8
@dbrree คุณอาจคัดลอกโค้ดที่จะไม่ทำงานเป็นForm1_MouseDownไม่ได้รับมอบหมายให้เป็นจริงกรณีที่มีการMouseDown Form1
Remon Ramy

2
หากคุณมีป้ายกำกับหรือไอคอน (หรือวัตถุพื้นหน้าอื่น ๆ !) ที่คุณกำลังดึงแบบฟอร์มเพื่อลากให้เพิ่มเหตุการณ์ที่มีการเลื่อนลงในรายการเหล่านี้ด้วย เหตุการณ์ของฟอร์มไม่สามารถมองทะลุวัตถุในฟอร์มได้
Paul Nelson

1
คุณต้องเพิ่มthis.MouseDown += ...ไปยังMain()ฟังก์ชั่นสำหรับแบบฟอร์ม
ริชาร์ดกัด

1
ทำงานอย่างมีเสน่ห์สำหรับฉัน !!!! ฉันต้องย้ายการจัดการเหตุการณ์ไปที่แผงควบคุมที่ฉันวางไว้แทนแบบฟอร์ม แต่มันใช้งานได้
จัสติน

54

อย่าทำสิ่งที่ยากเกินความจำเป็น ฉันเจอตัวอย่างโค้ดมากมายที่ให้คุณลากแบบฟอร์มไปรอบ ๆ (หรือ Control แบบอื่น) และหลายคนมีข้อเสีย / ผลข้างเคียง โดยเฉพาะอย่างยิ่งที่พวกเขาหลอกให้ Windows คิดว่า Control on a form เป็นรูปแบบที่แท้จริง

ที่ถูกกล่าวว่านี่คือตัวอย่างของฉัน ฉันจะใช้มันตลอดเวลา. ฉันต้องการทราบด้วยว่าคุณไม่ควรใช้สิ่งนี้.Invalidate (); อย่างที่คนอื่นชอบทำเพราะทำให้ฟอร์มสั่นไหวในบางกรณี และในบางกรณีก็ทำเช่นนี้รีเฟรช ใช้ this.Update ฉันไม่มีปัญหากะพริบใด ๆ :

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 ... มองหาคำตอบนี้มาเกือบสองชั่วโมงแล้ว .... ฉันยังใหม่กับ c # แม้ว่าคุณจะทำให้ฉันประหลาดใจ!
PrinceOfRavens

นี่เป็นวิธีที่ฉันคิดอย่างแน่นอนปัญหาเดียวของฉันคือมันเป็นรถที่เป็นนรกทั้งหมดจนกว่าฉันจะอ่านว่าคุณทำอย่างไร ขอบคุณเพื่อน
EasyBB

อันนี้ทำงานได้ดีกว่าสำหรับฉันมากกว่าตัวอย่างเล็ก ๆ ที่น่าทึ่งเหนือการแทนที่ WndProc โปรดทราบว่า WndProc ทำงานได้ ... มันหยุดสิ่งอื่น ๆ ไม่ให้ทำงาน ขอบคุณ!
Mmm

อาจเป็นตัวเลือกที่ดีที่สุดที่นี่เนื่องจากใช้งานได้ดีและไม่ต้องพึ่งพา WndProc (สามารถย้ายไปยังแพลตฟอร์มอื่นด้วย Mono ได้อย่างง่ายดาย) สามารถปรับปรุงได้โดยเปลี่ยนสถานะเป็นขยายใหญ่สุด / ปกติถ้า Y <10
Sylverdrag

มันไม่ทำงานบนโมโนเปลี่ยน. net 4.5.2 เป็นโมโน / เน็ต 4.5 แล้วก็ยังใช้ไม่ได้ พยายามหาทางแก้ไขอยู่ในขณะนี้
Roger Deep

35

อีกวิธีที่ง่ายกว่าในการทำสิ่งเดียวกัน

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
ใครก็ตามที่ทำให้เป็นไปได้ว่าคุณสามารถย้ายแบบฟอร์มโดยถือเครื่องมือเฉพาะ (เช่นป้ายกำกับ)
Thomas W

2
ถ้าดับเบิลคลิกฟอร์มจะขยายใหญ่สุด อ่านที่นี่ด้วยและที่นี่หยุดคลิกขวา
Sebastiano

19

ใช้ MouseDown, MouseMove และ MouseUp คุณสามารถตั้งค่าสถานะตัวแปรสำหรับสิ่งนั้น ฉันมีตัวอย่าง แต่คิดว่าคุณต้องแก้ไข

ฉันกำลังเข้ารหัสการทำงานของเมาส์ไปยังแผงควบคุม เมื่อคุณคลิกแผงแล้วแบบฟอร์มของคุณจะย้ายไปด้วย

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

มันคือ. ดังที่ได้กล่าวไปแล้วในที่อื่นสิ่งนี้อาศัยรูปแบบที่ยังคงสร้างเหตุการณ์ MouseMove ในกรณีง่ายๆสมมติว่าคุณไล่ระดับแบบฟอร์มที่แถวพิกเซลบนสุดแล้วลากขึ้น จะไม่มีอะไรเกิดขึ้นแม้ว่าแบบฟอร์มจะกระโดดไปมาทันทีที่คุณเลื่อนเมาส์กลับไปที่มัน
Joey

13

WPF เท่านั้น


ไม่มีรหัสที่แน่นอนในมือ แต่ในโครงการล่าสุดฉันคิดว่าฉันใช้เหตุการณ์ MouseDown และใส่สิ่งนี้:

frmBorderless.DragMove();

วิธี Window.DragMove (MSDN)


2
นั่นคือ WPF แม้ว่า โอเค OP ไม่ได้ระบุอย่างแน่นอน
Joey

ใช่ซึ่งเป็นสิ่งที่ฉันลืมเกี่ยวกับโครงการที่กำลังทำอยู่ ฉันเพิ่งดูแบบฟอร์มและไม่สามารถใช้งานได้ ขออภัย!
คริส

3
@Chris สิ่งนี้ใช้ได้กับฉันในโครงการ WPF ขอบคุณที่ไม่ลบคำตอบ
Rembunator

7

อ้างอิง ลิงค์วิดีโอ

สิ่งนี้ผ่านการทดสอบและเข้าใจง่าย

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
รหัสนี้ใช้งานได้ แต่เข้าใจง่ายอย่างไร? ฉันไม่รู้อะไรเลยในส่วนรหัสนี้นอกจากคำสั่ง switch!
Jamshaid Kamran

นี่เป็นการWM_NCHITTESTปลอมตัว
Andreas Rejbrand

4

ไม่มีทรัพย์สินที่คุณสามารถพลิกเพื่อให้สิ่งนี้เกิดขึ้นได้อย่างน่าอัศจรรย์ ดูเหตุการณ์ที่เกิดขึ้นสำหรับแบบฟอร์มและมันจะกลายเป็นธรรมเล็กน้อยเพื่อดำเนินการนี้โดยการตั้งค่าและthis.Top this.Leftโดยเฉพาะคุณจะต้องการที่จะมองไปที่MouseDown, และMouseUpMouseMove


ฉันคิดว่าฉันจะต้องใช้เหตุการณ์เหล่านั้น แต่ฉันไม่แน่ใจว่าจะทำอย่างไรกับเหตุการณ์เหล่านั้น เมื่อเหตุการณ์ MouseDown ถูกเรียกฉันจะอนุญาตให้ย้ายแบบฟอร์มได้อย่างไร
ผู้ใช้

1
เมื่อวางเมาส์ลงคุณจะตั้งค่าสถานะและจัดเก็บพิกัดฐาน เมื่อเลื่อนเมาส์ - หากตั้งค่าสถานะ - คุณปรับด้านบนและด้านซ้ายด้วยค่าชดเชยของพิกัดเมาส์ใหม่ เมื่อเลื่อนเมาส์ขึ้นคุณจะล้างค่าสถานะ
Murph

อย่างไรก็ตามคุณสามารถทำได้ด้วย Windows API ค่อนข้างง่ายซึ่งไม่ได้ขึ้นอยู่กับการรับเหตุการณ์เมาส์ วิธีนี้จะล้มเหลวหากคุณจับพิกเซลที่ขอบด้านบนสุดของแบบฟอร์มแล้วลากขึ้นด้านบน
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;
  }
}

สิ่งนี้สามารถแก้ปัญหาของคุณได้ ....


คุณยังสามารถใช้ "e.Location" แทน Control.MousePosition
Reza Taibur

4

https://social.msdn.microsoft.com/Forums/vstudio/en-US/d803d869-68e6-46ff-9ff1-fabf78d6393c/how-to-make-a-borderless-form-in-c?forum=csharpgeneral

รหัสบิตนี้จากลิงค์ด้านบนทำเคล็ดลับในกรณีของฉัน :)

protected override void OnMouseDown(MouseEventArgs e)  

{
      base.OnMouseDown(e);
      if (e.Button == MouseButtons.Left)
      {
        this.Capture = false;
        Message msg = Message.Create(this.Handle, 0XA1, new IntPtr(2), IntPtr.Zero);
        this.WndProc(ref msg);
      }
}

4

วิธีที่ดีที่สุดที่ฉันพบ (แก้ไขแน่นอน)

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

หากต้องการใช้การลากกับตัวควบคุมเพียงแค่ใส่สิ่งนี้หลังจาก InitializeComponent ()

AddDrag(NameOfControl);

4

มันได้ผลสำหรับฉัน

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

ยินดีต้อนรับสู่กองมากเกิน - ทัวร์กรุณาตรวจสอบวิธีการคำตอบ คุณต้องให้คำอธิบายว่ารหัสนี้แก้ปัญหาของ OP "โปสเตอร์ดั้งเดิม" ได้อย่างไร จะเป็นประโยชน์มากขึ้นสำหรับทุกคนที่ตรวจสอบคำตอบของคุณ
Mauricio Arias Olave

2

สำหรับ. NET Framework 4

คุณสามารถใช้this.DragMove()สำหรับMouseDownเหตุการณ์ของส่วนประกอบ (mainLayout ในตัวอย่างนี้) ที่คุณใช้ลาก

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

2

วิธีที่ง่ายที่สุดคือ:

ขั้นแรกให้สร้างป้ายชื่อ label1 ไปที่เหตุการณ์ของ label1> เหตุการณ์เมาส์> Label1_Mouse Move และเขียนสิ่งเหล่านี้:

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

2

ฉันพยายามสร้างหน้าต่างแบบไร้ขอบที่สามารถเคลื่อนย้ายได้ซึ่งมีการควบคุม WPF Element Host และการควบคุมผู้ใช้ WPF

ฉันลงเอยด้วยแผงสแต็กที่เรียกว่า StackPanel ในการควบคุมผู้ใช้ WPF ของฉันซึ่งดูเหมือนจะเป็นเรื่องที่สมเหตุสมผลที่จะลองคลิกเพื่อย้าย การลองใช้โค้ดของ Junmats ใช้งานได้เมื่อฉันเลื่อนเมาส์ช้าๆ แต่ถ้าฉันเลื่อนเมาส์เร็วขึ้นเมาส์จะเคลื่อนออกจากแบบฟอร์มและแบบฟอร์มจะติดค้างอยู่ที่ไหนสักแห่ง

สิ่งนี้ได้รับการปรับปรุงให้ดีขึ้นสำหรับคำตอบของเขาสำหรับสถานการณ์ของฉันโดยใช้ CaptureMouse และ ReleaseCaptureMouse และตอนนี้เมาส์ไม่เคลื่อนออกจากแบบฟอร์มในขณะที่เคลื่อนย้ายแม้ว่าฉันจะย้ายอย่างรวดเร็ว

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

เนื่องจากคำตอบบางคำไม่อนุญาตให้ลากการควบคุมเด็กได้ฉันจึงได้สร้างคลาสตัวช่วยเล็กน้อย มันควรจะผ่านฟอร์มระดับบน สามารถทำให้เป็นแบบทั่วไปได้มากขึ้นหากต้องการ

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

นอกจากนี้หากคุณต้องการ DoubleClick และทำให้แบบฟอร์มของคุณใหญ่ขึ้น / เล็กลงคุณสามารถใช้คำตอบแรกสร้างตัวแปร int ส่วนกลางเพิ่ม 1 ทุกครั้งที่ผู้ใช้คลิกที่ส่วนประกอบที่คุณใช้ในการลาก ถ้าอย่างvariable == 2นั้นทำให้ฟอร์มของคุณใหญ่ขึ้น / เล็กลง ใช้ตัวจับเวลาทุกๆครึ่งวินาทีหรือหนึ่งวินาทีเพื่อให้คุณvariable = 0;


1

การเพิ่มMouseLeftButtonDownตัวจัดการเหตุการณ์ไปยัง MainWindow ทำงานให้ฉัน

ในฟังก์ชันเหตุการณ์ที่สร้างขึ้นโดยอัตโนมัติให้เพิ่มรหัสด้านล่าง:

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

1

ฉันกำลังขยายโซลูชันจาก jay_t55 ด้วยอีกวิธีหนึ่งToolStrip1_MouseLeaveที่จัดการกับเหตุการณ์ที่เมาส์เคลื่อนที่อย่างรวดเร็วและออกจากพื้นที่

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

ฉันลองทำตามขั้นตอนต่อไปนี้และทำการเปลี่ยนแปลงล่วงหน้าหน้าต่างโปร่งใสของฉันไม่ถูกแช่แข็งอีกต่อไป แต่สามารถเคลื่อนย้ายได้ !! (ทิ้งโซลูชันที่ซับซ้อนอื่น ๆ ทั้งหมดข้างต้น ... )

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

คำตอบนี้มีไว้สำหรับ WPF คำถามเกี่ยวกับ WinForms
Ray

0

แบบฟอร์ม 1 (): new Moveable(control1, control2, control3);

ชั้น:

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

จะเป็นการดีถ้าคุณสนับสนุนคำตอบของคุณพร้อมคำอธิบาย :)
Mustafamg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.