在 Windows Forms 中實作圖片的滑鼠拖曳移動,核心在於處理 MouseDown、MouseMove、MouseUp 三個事件,並配合自訂游標提升使用者體驗。本文提供完整可執行程式碼,並說明關鍵座標轉換邏輯。
PictureBox 的位置 (pClicked),切換為「抓取」游標,設定拖曳旗標。pClicked 得到新的 PictureBox.Location。關鍵轉換:this.PointToClient(pictureBox1.PointToScreen(new Point(e.X, e.Y))) 將 PictureBox 內的滑鼠座標轉為表單用戶區座標。
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsApplication2
{
public partial class Form1 : Form
{
// Base64 編碼的游標資料(省略內容,實際使用請替換為有效資料)
static string strDown = @"AAACAAEAICAAAAsACQAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAIAAAAA...";
static string strUp = @"AAACAAEAICAAAAoACAAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAIAAA...";
Cursor curUp, curDown;
bool bDragging = false;
Point pClicked;
public Form1()
{
InitializeComponent();
curUp = new Cursor(new MemoryStream(Convert.FromBase64String(strUp)));
curDown = new Cursor(new MemoryStream(Convert.FromBase64String(strDown)));
pictureBox1.Cursor = curUp;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
bDragging = true;
pClicked = new Point(e.X, e.Y);
pictureBox1.Cursor = curDown;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (bDragging)
{
Point screenPos = pictureBox1.PointToScreen(new Point(e.X, e.Y));
Point clientPos = this.PointToClient(screenPos);
clientPos.Offset(-pClicked.X, -pClicked.Y);
pictureBox1.Location = clientPos;
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
bDragging = false;
pictureBox1.Cursor = curUp;
}
}
}
MouseMove 中加入邏輯防止圖片移出表單可視區域。List<PictureBox> 並處理 Z-Order(BringToFront/SendToBack)。DoubleBuffered 或自繪 (OnPaint) 避免閃爍。另一種常見做法是呼叫 Windows API SendMessage(hWnd, WM_SYSCOMMAND, SC_MOVE, 0) 模擬標題列拖曳,但該方法需表單無邊框 (FormBorderStyle.None) 且游標回饋較差。本文採用的純 managed 程式碼方案更穩定、可攜且易於擴充。