C# – Những cách di chuyển form không có thanh tiêu đề (title bar)

Đây là giải pháp khi bạn tạo một Form không có thanh tiêu đề và muốn dùng chuột để di chuyển tại vị trí bất kì. Bạn cũng có thể áp dụng các phương pháp này để di chuyển một control trong Form.

Cách 1: Sử dụng Windows API

Nguyên lý của cách này là giả lập thông điệp chuột được nhấn lên title bar của form và di chuyển. Để thực hiện ta cần dùng đến Windows API SendMessage() để gửi thông điệp đến cửa sổ cùng với hai tham số để xác định thông điệp là WM_NCLBUTTONDOWN và HTCAPTION.

Để khai báo hàm SendMessage(), ta cần thêm namespace System.Runtime.InteropServices và dùng attribute [DllImport]:

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

hWnd: handle của cửa sổ sẽ được nhận thông điệp.

Msg: định danh của thông điệp, trong ví dụ này là WM_NCLBUTTONDOWN. Xem System-Defined Messages.

wParam và lParam: các thông tin đi kèm với thông điệp.

Ta sẽ sử dụng hàm này trong sự kiện MouseDown của form. Nhưng trước khi gọi hàm này, ta phải đảm bảo form sẽ không bắt giữ và nhận các input của chuột bằng cách đặt property Capture = false.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        const int WM_NCLBUTTONDOWN = 0xA1;
        const int HT_CAPTION = 0x2;

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

        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {

            if (e.Button == MouseButtons.Left)
            {
                this.Capture = false;
                SendMessage(this.Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
            }
            base.OnMouseDown(e);
        }

    }
}

Cách 2: Tính toán và thay đổi vị trí form theo tọa độ chuột
Phương pháp này rất đơn giản và không cần dùng đến Windows API. Ta chỉ cần lưu lại vị trí khi chuột nhấn xuống (MouseDown) và khi chuột di chuyển, ta sẽ tính toán lại vị trí form dựa vào sự thay đổi giữa vị trí mới và vị trí của của chuột. Sử dụng thêm một biến boolean isMouseDown để xác định trạng thái của chuột.

using System.Windows.Forms;
using System.Drawing;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        bool isMouseDown;

        int xLast;
        int yLast;

        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            isMouseDown = true;
            xLast = e.X;
            yLast = e.Y;

            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (isMouseDown)
            {
                int newY = this.Top + (e.Y - yLast);
                int newX = this.Left + (e.X - xLast);

                this.Location = new Point(newX, newY);
            }

            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            isMouseDown = false;

            base.OnMouseUp(e);
        }

    }
}

Cách 3: Thay đổi thông điệp của cửa sổ

Phương pháp này được thực hiện bằng cách thay đổi thông điệp nhấn chuột trên client area (vùng trong cửa sổ trừ title bar) thành thông điệp trên title bar. Bằng cách override phương thức WndProc(), ta sẽ thay đổi property Message.Result từ HTCLIENT thành HTCAPTION. Property này tương ứng với tham số wParam trong hàm SensMessage().

Đây là cách tốt nhất và đơn giản nhất mà bạn nên áp dụng.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        const int WM_NCHITTEST = 0x84;
        const int HTCLIENT = 0x1;
        const int HTCAPTION = 0x2;

        public Form1()
        {
            InitializeComponent();
        }
        protected override void WndProc(ref Message message)
        {
            base.WndProc(ref message);

            if (message.Msg == WM_NCHITTEST && (int)message.Result == HTCLIENT)
                message.Result = (IntPtr)HTCAPTION;
        }
    }
}