Sign in Join
Wiki copytemporary

Drag and Drop sample/ru: what changed

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

OldNewLine
11Перетаскивание и бросание элементов интерфейса - это обычная операция, которая делает интерфейс удобным для пользователя: пользователь может перетаскивать информацию в элементы управления вместо того, чтобы вводить и т.д.
22
33В следующем примере объясняются основы перетаскивания. Для получения подробной информации обратитесь к другим статьям вики и справочной документации.
44
55Обратите внимание, поскольку LCL частично совместим с Delphi VCL, некоторые статьи/примеры о перетаскивании Delphi могут также относиться к LCL.
66
77## Drag and Drop
88
99Несмотря на простоту работы с точки зрения пользователя, неопытному разработчику она может доставить немало хлопот.
1010
1111Для кода операция перетаскивания всегда состоит как минимум из трех шагов:
1212
13131. Некоторый элемент управления запускает операцию перетаскивания. Он называется Source (Источником)
14142. Пользователь таскает курсор мыши над другими элементами управления или над самим источником. Теперь перетаскиваемый элемент управления должен решить, может ли он принимать перетаскиваемые данные.
15153. Бросание происходит, если элемент управления соглашается принять перетаскиваемые данные. Принимающий элемент управления называется Sender (Приемник).
1616
1717Для упрощения перетаскивания в LCL предусмотрен «автоматический» режим. Это не означает, что LCL выполняет все операции перетаскивания за вас, но он будет обрабатывать низкоуровневое управление объектами перетаскивания (которое не рассматривается в этой статье).
1818
1919## Примеры
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
2623Пример охватывает функцию автоматического перетаскивания между двумя элементами управления (Edit-\>Treeview), а также внутри одного элемента управления (Treeview-\>Treeview).
2724
2825- Запустите новое приложение.
2926- Поместите компонент TreeView и Edit в форму.
3027- Включите автоматический режим перетаскивания (Automatic drag-and-drop) для TreeView и редактирования в инспекторе объектов:
3128
3229**DragMode: dkAutomatic**
3330
3431Теперь вы можете запустить приложение и попробовать перетащить что-нибудь. У вас пока не должно ничего работать. Но если вы нажмете левую кнопку мыши в Treeview, вы, вероятно, увидите, что значок курсора изменился, но при отпускании мыши ничего не происходит.
3532
3633### Перетаскивание между элементами управления
3734
3835Сделаем операцию перетаскивания между Edit и TreeView. Там содержимое Edit будет «перетащено» в TreeView и будет создан новый узел дерева.
3936
4037Чтобы инициировать перетаскивание, у элементов управления есть специальный метод: `BeginDrag()`
4138
4239Создайте событие OnMouseDown для Edit:
4340
4441``` pascal
4542procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;
4643 Shift: TShiftState; X, Y: Integer);
4744begin
4845 if Button = mbLeft then {проверяем, была ли нажата левая кнопка мыши}
4946 Edit1.BeginDrag(true); {начинаем операцию перетаскивания}
5047end;
5148```
5249
5350Если вы запустите приложение прямо сейчас и попытаетесь перетащить его, вы заметите, что Edit запускает операцию, но по-прежнему ничего не происходит, когда вы пытаетесь тащить элемент к TreeView.
5451
5552Это потому, что TreeView не принимает данные. Ни один из элементов управления не принимает данные по умолчанию, поэтому вам всегда нужно предоставлять соответствующий обработчик событий.
5653
5754Создайте код в событии TreeView.OnDragOver:
5855
5956``` pascal
6057procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
6158 State: TDragState; var Accept: Boolean);
6259begin
6360 Accept := true;
6461end;
6562```
6663
6764Конечно, в некоторых случаях TreeView может запретить перетаскивание (если Source или данные не могут быть обработаны), но на данный момент TreeView всегда принимает перетаскивание.
6865
6966Запустите приложение и протестируйте. Теперь все должно быть лучше, хотя при бросании элемента все равно ничего не происходит.
7067
7168Создайте код в событии TreeView.OnDragDrop:
7269
7370``` pascal
7471procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
7572var
7673 tv : TTreeView;
7774 iNode : TTreeNode;
7875begin
7976 tv := TTreeView(Sender); { Sender - это TreeView, где данные удаляются }
8077 iNode := tv.GetNodeAt(x,y); { х, y - координаты перетаскивания (относительно Sender) }
8178 { поскольку Sender - это TreeView, мы можем оценить }
8279 { дерево в координатах X, Y }
8380
8481 { TreeView также может быть Source'ом! Так что мы должны убедиться, }
8582 { что этим Source'ом является TEdit, до получения им текста }
8683
8784 if Source is TEdit then
8885 tv.Items.AddChild(iNode, TEdit(Source).Text); {Теперь мы можем добавить новый узел с текстом из Source }
8986end;
9087```
9188
9289Запускаем и тестируем. Теперь он должен работать. Перетаскивание текста из Edit в TreeView должно создать новый узел.
9390
9491### Перетаскивание внутри элемента управления
9592
9693Sender и Source могут быть одним и тем же элементом управления! Это ни в коем случае не запрещено. Давайте добавим возможность TextView изменять расположение его узлов. Поскольку TextView находится в автоматическом режиме DragMode, вам не нужно начинать перетаскивание с помощью `DragBegin()`. Он запускается автоматически при перемещении мыши с удержанием левой кнопки.
9794
9895Убедитесь, что у вас "Accept:=true;" внутри операции DragOver для источника TreeView.
9996
10097Измените обработчик события DragDrop следующим образом:
10198
10299``` pascal
103100procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
104101var
105102 tv : TTreeView;
106103 iNode : TTreeNode;
107104begin
108105 tv := TTreeView(Sender); { Sender - это TreeView, где данные удаляются }
109106 iNode := tv.GetNodeAt(x,y); { х, y - координаты перетаскивания (относительно Sender) }
110107 {поскольку Sender - это TreeView, мы можем оценить }
111108 { дерево в координатах X, Y }
112109
113110 { TreeView также может быть Source'ом! Так что мы должны убедиться, }
114111 { что этим Source'ом является TEdit, до получения им текста }
115112 if Source is TEdit then
116113 tv.Items.AddChild(iNode, TEdit(Source).Text) {Теперь мы можем добавить новый узел с текстом из Source }
117114
118115 else if Source = Sender then begin { бросание элемента интерфейса происходит внутри TreeView }
119116 if Assigned(tv.Selected) and { проверяем, был ли выбран какой-либо узел }
120117 (iNode <> tv.Selected) then { и мы переходим к другому узлу }
121118 begin
122119 if iNode <> nil then
123120 tv.Selected.MoveTo(iNode, naAddChild) { завершаем операцию перетаскивания, переместив выбранный узел }
124121 else
125122 tv.Selected.MoveTo(iNode, naAdd); { завершаем операцию перетаскивания, переместившись в корень TreeView }
126123 end;
127124 end;
128125end;
129126```
130127
131128Вот и все. Если вы запустите приложение сейчас, у вас должны работать обе функции.
132129
133130- Добавление нового узла путем перетаскивания текста из Edit в TreeView
134131- Перетаскивание узлов внутри древовидной структуры
135132
136133## Подсказки
137134
138135- Можете (не можете) ли вы использовать некоторые глобальные данные, чтобы проверить, что сейчас перетаскивается? Используйте для этого не глобальные переменные, а только поля вашего класса формы.
139136 - Помещайте туда данные при запуске перетаскивания
140137 - Проверяйте данные во время перетаскивания элемента управления и соответствующим образом изменяйте Accept-флаг элемента .
141138 - Читайте и используйте данные в событии перетаскивания
142139
143140## Перетаскивание из других приложений
144141
145142Вы можете перетащить/отпустить
146143
147144### Файлы
148145
149146Файлы можно легко бросать и обрабатывать, реализуя событие FormDropFiles после того, как для формы свойство AllowDropFiles задано как `True`:
150147
151148``` pascal
152149procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of String);
153150var FileName : String;
154151begin
155152 for FileName in FileNames do
156153 begin
157154 ShowMessage(FileName);
158155 end;
159156end;
160157```
161158
162159------------------------------------------------------------------------
163160
164161Прим.перев.: Наиболее наглядно пример реализован [здесь](http://lazplanet.blogspot.com/2013/05/drag-drop-files-lazarus-form.html).
165162
166163------------------------------------------------------------------------
167164
168165### Текст и т.д.
169166
170167Вы можете перетащить, например текст из другого приложения (например, блокнота) в элемент управления в вашем приложении. Способ реализации зависит от платформы.
171168
172169#### Windows
173170
174171Этот код позволяет перетаскивать выделенный текст из других приложений в элемент управления редактированием.
175172
176173``` pascal
177174unit Unit1;
178175
179176{$mode objfpc}{$H+}
180177
181178interface
182179
183180uses
184181 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
185182 Windows, ActiveX, ComObj;
186183
187184type
188185
189186 { TForm1 }
190187
191188 TForm1 = class(TForm, IDropTarget)
192189 Memo1: TMemo;
193190 procedure FormCreate(Sender: TObject);
194191 procedure FormDestroy(Sender: TObject);
195192 private
196193 // IDropTarget
197194 function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
198195 function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
199196 function DragLeave: HResult;StdCall;
200197 function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD):HResult;StdCall;
201198 // IUnknown
202199 // Ignore referance counting
203200 function _AddRef: Integer; stdcall;
204201 function _Release: Integer; stdcall;
205202 public
206203 { public declarations }
207204 end;
208205
209206var
210207 Form1: TForm1;
211208
212209implementation
213210
214211{$R *.lfm}
215212
216213{ TForm1 }
217214
218215procedure TForm1.FormCreate(Sender: TObject);
219216begin
220217 OleInitialize(nil);
221218 OleCheck(RegisterDragDrop(Handle, Self));
222219end;
223220
224221procedure TForm1.FormDestroy(Sender: TObject);
225222begin
226223 RevokeDragDrop(Handle);
227224 OleUninitialize;
228225end;
229226
230227function TForm1.DragEnter(const dataObj: IDataObject; grfKeyState: DWORD;
231228 pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
232229begin
233230 dwEffect := DROPEFFECT_COPY;
234231 Result := S_OK;
235232end;
236233
237234function TForm1.DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD
238235 ): HResult; StdCall;
239236begin
240237 dwEffect := DROPEFFECT_COPY;
241238 Result := S_OK;
242239end;
243240
244241function TForm1.DragLeave: HResult; StdCall;
245242begin
246243 Result := S_OK;
247244end;
248245
249246function TForm1._AddRef: Integer; stdcall;
250247begin
251248 Result := 1;
252249end;
253250
254251function TForm1._Release: Integer; stdcall;
255252begin
256253 Result := 1;
257254end;
258255
259256function TForm1.Drop(const dataObj: IDataObject; grfKeyState: DWORD;
260257 pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
261258var
262259 aFmtEtc: TFORMATETC;
263260 aStgMed: TSTGMEDIUM;
264261 pData: PChar;
265262begin
266263 {Убеждаемся, что рендеринг данных доступен}
267264 if (dataObj = nil) then
268265 raise Exception.Create('IDataObject-Указатель недействителен!');
269266 with aFmtEtc do
270267 begin
271268 cfFormat := CF_TEXT;
272269 ptd := nil;
273270 dwAspect := DVASPECT_CONTENT;
274271 lindex := -1;
275272 tymed := TYMED_HGLOBAL;
276273 end;
277274 {Получаем данные}
278275 OleCheck(dataObj.GetData(aFmtEtc, aStgMed));
279276 try
280277 {Заблокируем дескриптор глобальной памяти, чтобы получить указатель на данные}
281278 pData := GlobalLock(aStgMed.hGlobal);
282279 { Заменяем текст }
283280 Memo1.Text := pData;
284281 finally
285282 {Завершаем с указателем}
286283 GlobalUnlock(aStgMed.hGlobal);
287284 {Освобождаем память}
288285 ReleaseStgMedium(aStgMed);
289286 end;
290287 Result := S_OK;
291288end;
292289
293290end.
294291```
295292
296293Источник на форуме: [этот тред](http://forum.lazarus.freepascal.org/index.php/topic,25769.msg156933.html#msg156933)
297294
298295## См. также
299296
300297- [[LCL Drag Drop/ru|LCL Drag Drop]]
301298
302299---
303300*From the Free Pascal wiki, page "Drag and Drop sample/ru", as it stood on 2022-01-02 after a change by FTurtle, 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