承接上篇,本文深入 TControl 三大拖放方法、標準化四階段開發流程,並以 TabSet 標籤拖曳重新排序為完整案例展示實作細節。
| 方法 | 用途 | 關鍵參數 |
|---|---|---|
BeginDrag(Immediate: Boolean) | 手動模式啟動拖放 | Immediate=True 立即開始;False 待移動 5 像素後開始(保留 OnClick) |
EndDrag(Drop: Boolean) | 結束拖放 | Drop=True 在當前位置放下(目標決定是否接受);False 取消 |
Dragging: Boolean | 判斷控件是否正被拖曳 | 唯讀屬性,常用於 OnDragOver 中依來源控件動態變更 UI |
procedure TForm1.Panel1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := False;
if (Source is TLabel) and (State = dsDragEnter) then
(Source as TLabel).EndDrag(False); // 強制取消
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
CheckBox1.DragMode := dmAutomatic;
CheckBox2.DragMode := dmAutomatic;
CheckBox3.DragMode := dmAutomatic;
end;
procedure TForm1.FormDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
if CheckBox1.Dragging then Color := clAqua;
if CheckBox2.Dragging then Color := clYellow;
if CheckBox3.Dragging then Color := clLime;
end;
DragMode := dmAutomatic 或 OnMouseDown 中呼叫 BeginDragOnDragOver 設定 Accept := True/False,可依 Source 型別、State 決定OnDragDrop 執行實際資料移轉/排序邏輯OnEndDrag 清理暫存狀態(Target <> nil 表示成功放下)需求: 執行期將標籤拖至新位置,Tabs 字串清單自動重新排序。
放置 TabSet1,DragMode := dmManual(手動控制)。
procedure TForm1.TabSet1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
DragItem: Integer;
begin
if Button = mbLeft then begin
DragItem := TabSet1.ItemAtPos(Point(X, Y)); // 取得點擊處標籤索引
if (DragItem > -1) and (DragItem < TabSet1.Tabs.Count) then
TabSet1.BeginDrag(False); // False:移動 5 像素後才開始,保留單擊選中功能
end;
end;
procedure TForm1.TabSet1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := (Source = TabSet1); // 僅接受自身標籤拖放
end;
procedure TForm1.TabSet1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
SrcIndex, DstIndex: Integer;
Temp: string;
begin
if Source = TabSet1 then begin
SrcIndex := TabSet1.Tag; // 儲存於 OnDragOver 或額外變數
DstIndex := TabSet1.ItemAtPos(Point(X, Y));
if (DstIndex >= 0) and (DstIndex <> SrcIndex) then begin
Temp := TabSet1.Tabs[SrcIndex];
TabSet1.Tabs.Delete(SrcIndex);
TabSet1.Tabs.Insert(DstIndex, Temp);
TabSet1.TabIndex := DstIndex; // 同步選中
end;
end;
end;
關鍵技巧: OnDragOver 中將 SrcIndex := TabSet1.ItemAtPos(Point(X, Y)) 暫存於 TabSet1.Tag 或表單私有欄位,供 OnDragDrop 使用。
procedure TForm1.TabSet1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
// Target <> nil 表示成功放下,可做後續處理
// 此處無額外清理需求
end;
DragCursor := crMultiDrag 或自訂游標資源TDataObject 實作 IDataObject 介面,支援 CF_TEXT/CF_HDROP 等格式DragKind := dkDock 可實現停靠/浮動面板(DockSite/DragKind 機制)OnDragOver 避免複雜運算,必要時用 BeginUpdate/EndUpdate| 事件 | 觸發時機 | 典型用途 |
|---|---|---|
OnDragOver | 拖曳進入/移動目標控件上空 | 設定 Accept、視覺回饋 |
OnDragDrop | 在目標控件上釋放滑鼠 | 資料移轉、排序、複製 |
OnEndDrag | 拖放結束(來源控件) | 清理、成功/失敗後處理 |
OnStartDrag | 拖放啟動前(僅 dmAutomatic) | 初始化拖放資料物件 |