Sign in Join
Wiki copytemporary

Drag and Drop sample/ru: what changed

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

Title: Drag and Drop sample/ru

OldNewLine
1+Перетаскивание и бросание элементов интерфейса - это обычная операция, которая делает интерфейс удобным для пользователя: пользователь может перетаскивать информацию в элементы управления вместо того, чтобы вводить и т.д.
2+
3+В следующем примере объясняются основы перетаскивания. Для получения подробной информации обратитесь к другим статьям вики и справочной документации.
4+
5+Обратите внимание, поскольку LCL частично совместим с Delphi VCL, некоторые статьи/примеры о перетаскивании Delphi могут также относиться к LCL.
6+
7+## Drag and Drop
8+
9+Несмотря на простоту работы с точки зрения пользователя, неопытному разработчику она может доставить немало хлопот.
10+
11+Для кода операция перетаскивания всегда состоит как минимум из трех шагов:
12+
13+1. Некоторый элемент управления запускает операцию перетаскивания. Он называется Source (Источником)
14+2. Пользователь таскает курсор мыши над другими элементами управления или над самим источником. Теперь перетаскиваемый элемент управления должен решить, может ли он принимать перетаскиваемые данные.
15+3. Бросание происходит, если элемент управления соглашается принять перетаскиваемые данные. Принимающий элемент управления называется Sender (Приемник).
16+
17+Для упрощения перетаскивания в LCL предусмотрен «автоматический» режим. Это не означает, что LCL выполняет все операции перетаскивания за вас, но он будет обрабатывать низкоуровневое управление объектами перетаскивания (которое не рассматривается в этой статье).
18+
19+## Примеры
20+
21+<figure>
22+<img src="DnDTest.PNG" title="DnDTest.PNG" />
23+<figcaption>DnDTest.PNG</figcaption>
24+</figure>
25+
26+Пример охватывает функцию автоматического перетаскивания между двумя элементами управления (Edit-\>Treeview), а также внутри одного элемента управления (Treeview-\>Treeview).
27+
28+- Запустите новое приложение.
29+- Поместите компонент TreeView и Edit в форму.
30+- Включите автоматический режим перетаскивания (Automatic drag-and-drop) для TreeView и редактирования в инспекторе объектов:
31+
32+**DragMode: dkAutomatic**
33+
34+Теперь вы можете запустить приложение и попробовать перетащить что-нибудь. У вас пока не должно ничего работать. Но если вы нажмете левую кнопку мыши в Treeview, вы, вероятно, увидите, что значок курсора изменился, но при отпускании мыши ничего не происходит.
35+
36+### Перетаскивание между элементами управления
37+
38+Сделаем операцию перетаскивания между Edit и TreeView. Там содержимое Edit будет «перетащено» в TreeView и будет создан новый узел дерева.
39+
40+Чтобы инициировать перетаскивание, у элементов управления есть специальный метод: `BeginDrag()`
41+
42+Создайте событие OnMouseDown для Edit:
43+
44+``` pascal
45+procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;
46+ Shift: TShiftState; X, Y: Integer);
47+begin
48+ if Button = mbLeft then {проверяем, была ли нажата левая кнопка мыши}
49+ Edit1.BeginDrag(true); {начинаем операцию перетаскивания}
50+end;
51+```
52+
53+Если вы запустите приложение прямо сейчас и попытаетесь перетащить его, вы заметите, что Edit запускает операцию, но по-прежнему ничего не происходит, когда вы пытаетесь тащить элемент к TreeView.
54+
55+Это потому, что TreeView не принимает данные. Ни один из элементов управления не принимает данные по умолчанию, поэтому вам всегда нужно предоставлять соответствующий обработчик событий.
56+
57+Создайте код в событии TreeView.OnDragOver:
58+
59+``` pascal
60+procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
61+ State: TDragState; var Accept: Boolean);
62+begin
63+ Accept := true;
64+end;
65+```
66+
67+Конечно, в некоторых случаях TreeView может запретить перетаскивание (если Source или данные не могут быть обработаны), но на данный момент TreeView всегда принимает перетаскивание.
68+
69+Запустите приложение и протестируйте. Теперь все должно быть лучше, хотя при бросании элемента все равно ничего не происходит.
70+
71+Создайте код в событии TreeView.OnDragDrop:
72+
73+``` pascal
74+procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
75+var
76+ tv : TTreeView;
77+ iNode : TTreeNode;
78+begin
79+ tv := TTreeView(Sender); { Sender - это TreeView, где данные удаляются }
80+ iNode := tv.GetNodeAt(x,y); { х, y - координаты перетаскивания (относительно Sender) }
81+ { поскольку Sender - это TreeView, мы можем оценить }
82+ { дерево в координатах X, Y }
83+
84+ { TreeView также может быть Source'ом! Так что мы должны убедиться, }
85+ { что этим Source'ом является TEdit, до получения им текста }
86+
87+ if Source is TEdit then
88+ tv.Items.AddChild(iNode, TEdit(Source).Text); {Теперь мы можем добавить новый узел с текстом из Source }
89+end;
90+```
91+
92+Запускаем и тестируем. Теперь он должен работать. Перетаскивание текста из Edit в TreeView должно создать новый узел.
93+
94+### Перетаскивание внутри элемента управления
95+
96+Sender и Source могут быть одним и тем же элементом управления! Это ни в коем случае не запрещено. Давайте добавим возможность TextView изменять расположение его узлов. Поскольку TextView находится в автоматическом режиме DragMode, вам не нужно начинать перетаскивание с помощью `DragBegin()`. Он запускается автоматически при перемещении мыши с удержанием левой кнопки.
97+
98+Убедитесь, что у вас "Accept:=true;" внутри операции DragOver для источника TreeView.
99+
100+Измените обработчик события DragDrop следующим образом:
101+
102+``` pascal
103+procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
104+var
105+ tv : TTreeView;
106+ iNode : TTreeNode;
107+begin
108+ tv := TTreeView(Sender); { Sender - это TreeView, где данные удаляются }
109+ iNode := tv.GetNodeAt(x,y); { х, y - координаты перетаскивания (относительно Sender) }
110+ {поскольку Sender - это TreeView, мы можем оценить }
111+ { дерево в координатах X, Y }
112+
113+ { TreeView также может быть Source'ом! Так что мы должны убедиться, }
114+ { что этим Source'ом является TEdit, до получения им текста }
115+ if Source is TEdit then
116+ tv.Items.AddChild(iNode, TEdit(Source).Text) {Теперь мы можем добавить новый узел с текстом из Source }
117+
118+ else if Source = Sender then begin { бросание элемента интерфейса происходит внутри TreeView }
119+ if Assigned(tv.Selected) and { проверяем, был ли выбран какой-либо узел }
120+ (iNode <> tv.Selected) then { и мы переходим к другому узлу }
121+ begin
122+ if iNode <> nil then
123+ tv.Selected.MoveTo(iNode, naAddChild) { завершаем операцию перетаскивания, переместив выбранный узел }
124+ else
125+ tv.Selected.MoveTo(iNode, naAdd); { завершаем операцию перетаскивания, переместившись в корень TreeView }
126+ end;
127+ end;
128+end;
129+```
130+
131+Вот и все. Если вы запустите приложение сейчас, у вас должны работать обе функции.
132+
133+- Добавление нового узла путем перетаскивания текста из Edit в TreeView
134+- Перетаскивание узлов внутри древовидной структуры
135+
136+## Подсказки
137+
138+- Можете (не можете) ли вы использовать некоторые глобальные данные, чтобы проверить, что сейчас перетаскивается? Используйте для этого не глобальные переменные, а только поля вашего класса формы.
139+ - Помещайте туда данные при запуске перетаскивания
140+ - Проверяйте данные во время перетаскивания элемента управления и соответствующим образом изменяйте Accept-флаг элемента .
141+ - Читайте и используйте данные в событии перетаскивания
142+
143+## Перетаскивание из других приложений
144+
145+Вы можете перетащить/отпустить
146+
147+### Файлы
148+
149+Файлы можно легко бросать и обрабатывать, реализуя событие FormDropFiles после того, как для формы свойство AllowDropFiles задано как `True`:
150+
151+``` pascal
152+procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of String);
153+var FileName : String;
154+begin
155+ for FileName in FileNames do
156+ begin
157+ ShowMessage(FileName);
158+ end;
159+end;
160+```
161+
162+------------------------------------------------------------------------
163+
164+Прим.перев.: Наиболее наглядно пример реализован [здесь](http://lazplanet.blogspot.com/2013/05/drag-drop-files-lazarus-form.html).
165+
166+------------------------------------------------------------------------
167+
168+### Текст и т.д.
169+
170+Вы можете перетащить, например текст из другого приложения (например, блокнота) в элемент управления в вашем приложении. Способ реализации зависит от платформы.
171+
172+#### Windows
173+
174+Этот код позволяет перетаскивать выделенный текст из других приложений в элемент управления редактированием.
175+
176+``` pascal
177+unit Unit1;
178+
179+{$mode objfpc}{$H+}
180+
181+interface
182+
183+uses
184+ Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
185+ Windows, ActiveX, ComObj;
186+
187+type
188+
189+ { TForm1 }
190+
191+ TForm1 = class(TForm, IDropTarget)
192+ Memo1: TMemo;
193+ procedure FormCreate(Sender: TObject);
194+ procedure FormDestroy(Sender: TObject);
195+ private
196+ // IDropTarget
197+ function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
198+ function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
199+ function DragLeave: HResult;StdCall;
200+ function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD):HResult;StdCall;
201+ // IUnknown
202+ // Ignore referance counting
203+ function _AddRef: Integer; stdcall;
204+ function _Release: Integer; stdcall;
205+ public
206+ { public declarations }
207+ end;
208+
209+var
210+ Form1: TForm1;
211+
212+implementation
213+
214+{$R *.lfm}
215+
216+{ TForm1 }
217+
218+procedure TForm1.FormCreate(Sender: TObject);
219+begin
220+ OleInitialize(nil);
221+ OleCheck(RegisterDragDrop(Handle, Self));
222+end;
223+
224+procedure TForm1.FormDestroy(Sender: TObject);
225+begin
226+ RevokeDragDrop(Handle);
227+ OleUninitialize;
228+end;
229+
230+function TForm1.DragEnter(const dataObj: IDataObject; grfKeyState: DWORD;
231+ pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
232+begin
233+ dwEffect := DROPEFFECT_COPY;
234+ Result := S_OK;
235+end;
236+
237+function TForm1.DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD
238+ ): HResult; StdCall;
239+begin
240+ dwEffect := DROPEFFECT_COPY;
241+ Result := S_OK;
242+end;
243+
244+function TForm1.DragLeave: HResult; StdCall;
245+begin
246+ Result := S_OK;
247+end;
248+
249+function TForm1._AddRef: Integer; stdcall;
250+begin
251+ Result := 1;
252+end;
253+
254+function TForm1._Release: Integer; stdcall;
255+begin
256+ Result := 1;
257+end;
258+
259+function TForm1.Drop(const dataObj: IDataObject; grfKeyState: DWORD;
260+ pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
261+var
262+ aFmtEtc: TFORMATETC;
263+ aStgMed: TSTGMEDIUM;
264+ pData: PChar;
265+begin
266+ {Убеждаемся, что рендеринг данных доступен}
267+ if (dataObj = nil) then
268+ raise Exception.Create('IDataObject-Указатель недействителен!');
269+ with aFmtEtc do
270+ begin
271+ cfFormat := CF_TEXT;
272+ ptd := nil;
273+ dwAspect := DVASPECT_CONTENT;
274+ lindex := -1;
275+ tymed := TYMED_HGLOBAL;
276+ end;
277+ {Получаем данные}
278+ OleCheck(dataObj.GetData(aFmtEtc, aStgMed));
279+ try
280+ {Заблокируем дескриптор глобальной памяти, чтобы получить указатель на данные}
281+ pData := GlobalLock(aStgMed.hGlobal);
282+ { Заменяем текст }
283+ Memo1.Text := pData;
284+ finally
285+ {Завершаем с указателем}
286+ GlobalUnlock(aStgMed.hGlobal);
287+ {Освобождаем память}
288+ ReleaseStgMedium(aStgMed);
289+ end;
290+ Result := S_OK;
291+end;
292+
293+end.
294+```
295+
296+Источник на форуме: [этот тред](http://forum.lazarus.freepascal.org/index.php/topic,25769.msg156933.html#msg156933)
297+
298+## См. также
299+
300+- [[LCL Drag Drop/ru|LCL Drag Drop]]
301+
302+---
303+*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