bool isDragging = false; // 是否按下滑鼠左鍵進入拖曳模式
Point dragStart; // 拖曳起始點 (螢幕/控制項座標)
Point dragCurrent; // 拖曳當前點
private void viewer_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
isDragging = true;
dragStart = e.Location;
}
}
private void viewer_MouseUp(object sender, MouseEventArgs e) {
isDragging = false;
}
private void viewer_MouseMove(object sender, MouseEventArgs e) {
if (!isDragging) return;
dragCurrent = e.Location;
// 計算位移量 (除以 10 降低靈敏度,負號反向使內容跟隨滑鼠移動)
int deltaX = -(dragCurrent.X - dragStart.X) / 10;
int deltaY = -(dragCurrent.Y - dragStart.Y) / 10;
// 取得捲軸範圍與當前位置
GetScrollRange(viewer.Handle, 0, out int minH, out int maxH); // SB_HORZ
GetScrollRange(viewer.Handle, 1, out int minV, out int maxV); // SB_VERT
int posH = GetScrollPos(viewer.Handle, 0);
int posV = GetScrollPos(viewer.Handle, 1);
// 計算新位置並邊界檢查
int newH = posH + deltaX;
if (newH >= minH && newH <= maxH) {
SetScrollPos(viewer.Handle, 0, newH, true);
PostMessage(viewer.Handle, 0x114, (IntPtr)(4 + 0x10000 * newH), IntPtr.Zero); // WM_HSCROLL + SB_THUMBPOSITION
}
int newV = posV + deltaY;
if (newV >= minV && newV <= maxV) {
SetScrollPos(viewer.Handle, 1, newV, true);
PostMessage(viewer.Handle, 0x115, (IntPtr)(4 + 0x10000 * newV), IntPtr.Zero); // WM_VSCROLL
}
}
[DllImport("user32.dll")] static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")] static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")] static extern bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
const int SB_HORZ = 0, SB_VERT = 1;
const int WM_HSCROLL = 0x114, WM_VSCROLL = 0x115;
const int SB_THUMBPOSITION = 4;
關鍵點:PostMessage 送出捲動訊息才會真正觸發控制項內容重繪;僅呼叫 SetScrollPos 僅移動捲軸滑塊,內容不動。