Sign in Join
Wiki copytemporary

Drag and Drop sample: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Drag and Drop is a common operation that makes the interface user friendly: a user can drag/drop information to controls instead of having to type etc.
22
33The following sample explains basics of drag and drop. For detailed information you should refer to other articles in the wiki and reference documentation.
44
55Please note, since LCL is partially compatible with Delphi's VCL, some articles/examples about Delphi drag-and-drop may also apply to LCL.
66
77## Drag and Drop
88
99Despite of the operation's simplicity from the user's point of view, it might give hard times to an inexperienced developer.
1010
1111For the code, Drag and Drop operation always consists of at least these 3 steps:
1212
13131. Some control starts the drag-and-drop operation. This is called the Source.
14142. User drags the mouse cursor around, above other controls or the Source itself. Now a dragged over control needs to decide, if it is able to accept the dragged data.
15153. Drop happens if a control agrees to accept the dragged data. The accepting control is called the Sender.
1616
1717To simplify drag-and-drop handling, the LCL provides "automatic" mode. It doesn't mean, that LCL does the whole drag-and-drop for you, but it will handle low-level drag object managing (which is not covered in this article).
1818
1919## Example
2020
21-<figure>
22-<img src="DnDTest.PNG" title="DnDTest.PNG" />
23-<figcaption>DnDTest.PNG</figcaption>
24-</figure>
21+![](/wiki-images/3/30/DnDTest.PNG)
2522
2623The example covers automatic drag-and-drop feature between 2 controls (Edit-\>Treeview) as well as inside a single control (Treeview-\>Treeview)
2724
2825- Start the new application.
2926- Put a TreeView component and Edit on the form.
3027- Enable Automatic drag-and-drop mode for TreeView by setting **DragMode: dkAutomatic**
3128- Edit in Object Inspector and add a few items
3229
3330Drag and drop still won't work, but launching the application and trying to drag the items in the Treeview will show cursor icon changes.
3431
3532### Dragging between controls
3633
3734Let's make a drag-and-drop operation between Edit and TreeView. There the content of Edit will be "dragged" to TreeView and a new tree node created.
3835
3936To initiate the drag, controls have a special method: `BeginDrag()`
4037
4138Create OnMouseDown event for the Edit:
4239
4340``` pascal
4441procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;
4542 Shift: TShiftState; X, Y: Integer);
4643begin
4744 if Button = mbLeft then {check if left mouse button was pressed}
4845 Edit1.BeginDrag(true); {starting the drag operation}
4946end;
5047```
5148
5249If you launch the application right now and try drag and drop, you'll notice that Edit starts the operation, but still nothing happens then you're trying to drop to TreeView.
5350
5451This is because TreeView doesn't accept the data. None of the controls accept data by default, so you'll always need to provide the proper event handler.
5552
5653Assign TreeView.OnDragOver operation:
5754
5855``` pascal
5956procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
6057 State: TDragState; var Accept: Boolean);
6158begin
6259 Accept := true;
6360end;
6461```
6562
6663Of course in some cases, TreeView might deny dropping (if Source or data cannot be handled), but for now, TreeView always accepts the dragging.
6764
6865Run application and test. Everything should be better now, though still nothing happens on drop.
6966
7067Assign TreeView.OnDragDrop operation:
7168
7269``` pascal
7370procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
7471var
7572 tv : TTreeView;
7673 iNode : TTreeNode;
7774begin
7875 tv := Sender as TTreeView; { Sender is TreeView where the data is being dropped }
7976 iNode := tv.GetNodeAt(x,y); { x,y are drop coordinates (relative to the Sender) }
8077 { since Sender is TreeView we can evaluate }
8178 { a tree at the X,Y coordinates }
8279
8380 { TreeView can also be a Source! So we must make sure }
8481 { that Source is TEdit, before getting its text }
8582 if Source is TEdit then
8683 tv.Items.AddChild(iNode, TEdit(Source).Text); {now, we can add a new node, with a text from Source }
8784end;
8885```
8986
9087Run and test. It should be working now. Dragging a text from Edit to TreeView should create a new node.
9188
9289### Dragging within a control
9390
9491Sender and Source can be the same control! It's not prohibited in any way. Let's add the ability to a TextView, to change its nodes location. Since TextView is in automatic DragMode, you don't need to start the drag by using `DragBegin()`. It's started automatically on mouse moved with left button hold.
9592
9693Make sure you have "Accept:=true;" inside the DragOver operation for TreeView source.
9794
9895Modify the DragDrop event handler to the following:
9996
10097``` pascal
10198procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
10299var
103100 tv : TTreeView;
104101 iNode : TTreeNode;
105102begin
106103 tv := Sender as TTreeView; { Sender is TreeView where the data is being dropped }
107104 iNode := tv.GetNodeAt(x,y); { x,y are drop coordinates (relative to the Sender) }
108105 { since Sender is TreeView we can evaluate }
109106 { a tree at the X,Y coordinates }
110107
111108 { TreeView can also be a Source! So we must make sure }
112109 { that Source is TEdit, before getting its text }
113110 if Source is TEdit then
114111 tv.Items.AddChild(iNode, TEdit(Source).Text) {now, we can add a new node, with a text from Source }
115112
116113 else if Source = Sender then begin { drop is happening within a TreeView }
117114 if Assigned(tv.Selected) and { check if any node has been selected }
118115 (iNode <> tv.Selected) then { and we're dropping to another node }
119116 begin
120117 if iNode <> nil then
121118 tv.Selected.MoveTo(iNode, naAddChild) { complete the drop operation, by moving the selectede node }
122119 else
123120 tv.Selected.MoveTo(iNode, naAdd); { complete the drop operation, by moving in root of a TreeView }
124121 end;
125122 end;
126123end;
127124```
128125
129126That's it. If you run the application now, you should have both features working.
130127
131128- Adding a new node by dragging text from Edit to TreeView
132129- Dragging nodes inside the treeview
133130
134131## Hints
135132
136133- You can?/cannot? use some global data to inspect what is being dragged now. Don't use global variables, but your form class's fields.
137134 - Put the data when you start the drag
138135 - Inspect the data, during control's drag over event, and modify Accept flag accordingly
139136 - Read and use the data on drop event
140137
141138## Dragging from other applications
142139
143140You can drag/drop
144141
145142### Files
146143
147144Files can be dropped and handled easily by implementing the FormDropFiles event, once AllowDropFiles on the form is set:
148145
149146``` pascal
150147procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of String);
151148var FileName : String;
152149begin
153150 for FileName in FileNames do
154151 begin
155152 ShowMessage(FileName);
156153 end;
157154end;
158155```
159156
160157### Text etc
161158
162159You can drag and drop e.g. text from another application (e.g. Notepad) to a control on your application. The way this is implemented is platform-dependent.
163160
164161#### Windows
165162
166163This code lets you drag and drop selected text from other applications onto an edit control.
167164
168165``` pascal
169166unit Unit1;
170167
171168{$mode objfpc}{$H+}
172169
173170interface
174171
175172uses
176173 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
177174 Windows, ActiveX, ComObj;
178175
179176type
180177
181178 { TForm1 }
182179
183180 TForm1 = class(TForm, IDropTarget)
184181 Memo1: TMemo;
185182 procedure FormCreate(Sender: TObject);
186183 procedure FormDestroy(Sender: TObject);
187184 private
188185 // IDropTarget
189186 function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
190187 function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
191188 function DragLeave: HResult;StdCall;
192189 function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD):HResult;StdCall;
193190 // IUnknown
194191 // Ignore referance counting
195192 function _AddRef: Integer; stdcall;
196193 function _Release: Integer; stdcall;
197194 public
198195 { public declarations }
199196 end;
200197
201198var
202199 Form1: TForm1;
203200
204201implementation
205202
206203{$R *.lfm}
207204
208205{ TForm1 }
209206
210207procedure TForm1.FormCreate(Sender: TObject);
211208begin
212209 OleInitialize(nil);
213210 OleCheck(RegisterDragDrop(Handle, Self));
214211end;
215212
216213procedure TForm1.FormDestroy(Sender: TObject);
217214begin
218215 RevokeDragDrop(Handle);
219216 OleUninitialize;
220217end;
221218
222219function TForm1.DragEnter(const dataObj: IDataObject; grfKeyState: DWORD;
223220 pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
224221begin
225222 dwEffect := DROPEFFECT_COPY;
226223 Result := S_OK;
227224end;
228225
229226function TForm1.DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD
230227 ): HResult; StdCall;
231228begin
232229 dwEffect := DROPEFFECT_COPY;
233230 Result := S_OK;
234231end;
235232
236233function TForm1.DragLeave: HResult; StdCall;
237234begin
238235 Result := S_OK;
239236end;
240237
241238function TForm1._AddRef: Integer; stdcall;
242239begin
243240 Result := 1;
244241end;
245242
246243function TForm1._Release: Integer; stdcall;
247244begin
248245 Result := 1;
249246end;
250247
251248function TForm1.Drop(const dataObj: IDataObject; grfKeyState: DWORD;
252249 pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
253250var
254251 aFmtEtc: TFORMATETC;
255252 aStgMed: TSTGMEDIUM;
256253 pData: PChar;
257254begin
258255 {Make certain the data rendering is available}
259256 if (dataObj = nil) then
260257 raise Exception.Create('IDataObject-Pointer is not valid!');
261258 with aFmtEtc do
262259 begin
263260 cfFormat := CF_TEXT;
264261 ptd := nil;
265262 dwAspect := DVASPECT_CONTENT;
266263 lindex := -1;
267264 tymed := TYMED_HGLOBAL;
268265 end;
269266 {Get the data}
270267 OleCheck(dataObj.GetData(aFmtEtc, aStgMed));
271268 try
272269 {Lock the global memory handle to get a pointer to the data}
273270 pData := GlobalLock(aStgMed.hGlobal);
274271 { Replace Text }
275272 Memo1.Text := pData;
276273 finally
277274 {Finished with the pointer}
278275 GlobalUnlock(aStgMed.hGlobal);
279276 {Free the memory}
280277 ReleaseStgMedium(aStgMed);
281278 end;
282279 Result := S_OK;
283280end;
284281
285282end.
286283```
287284
288285Source forum: <http://forum.lazarus.freepascal.org/index.php/topic,25769.msg156933.html#msg156933>
289286
290287## See also
291288
292289- [[LCL Drag Drop]]
293290
294291---
295292*From the Free Pascal wiki, page "Drag and Drop sample", as it stood on 2023-12-14 after a change by Alextpp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history