Пособие-самоучитель on-line "Visual Basic с нуля"
Глава 18. Технология Drag&Drop (перетаскивание) в пределах формы. События перетаскивания. Автоматическое перетаскивание. Метод Drag.
Скачать исходник примера DragDrop
Дата создания 03.04.2005 {Автор 4us}

При работе с приложениями Widows часто используется такой прием, когда юзер может нажать над объектом левую кнопку мыши, и не отпуская ее переместить мышь в другое место. При этом образ объекта следует за курсором мыши. При отпускании кнопки объект перемещается в новое место (если это предусмотрено программистом). Такая технология называется Drag & Drop - перетаскивание (или перетащил и оставил).

Использование этой технологии, по-моему является роскошью, однако придает программе профессиональный вид, а иногда даже делает интерфейс более удобным для пользователя.
Реализовать это довольно просто. Но прежде всего надо уяснить себе следующее:
Объект может быть взят только оттуда и перетащен только туда, как это определил программист. Иными словами, для реализации этой технологии должен быть объект-источник Drag и объект-приемник Drop и с каждым объектом, чтобы он поддерживал Drag&Drop, необходимо поработать отдельно.

Для реализации этой технологии мы имеем небогатый выбор событий. Их всего два, но этого вполне достаточно, чтобы внести неразбериху в твой проект, если в нем уже имеются процедуры событий обработки мыши или клавиатуры. Поэтому, как мне ни грустно говорить,тебе надо внимательно с ними ознакомиться.


Private Sub Объект_DragDrop([индекс As Integer,]source As Control, x As Single, y As Single)



Происходит при завершении операции drag&drop, т.е. перетаскиваемый объект находится в зоне объекта-приемника и кнопка мыши отпускается.
Индекс
определяет элемент массива , если имеется массив объектов.
source -аргумент, определяющий перетаскиваемый элемент (содержимое). К нему могут добавляться методы и свойства (Sourse.Tag, Sourse.Visible и т.п.).
Если используется несколько объектов для перетаскивания, то для определения типа элемента-приемника используется ключевое слово TypeOf с оператором If, а для однозначного определения объекта используется свойство Tag.
x, y - текущие координаты мыши на форме или объекте. Всегда измеряются в единицах объекта-приемника.

Событие используется для создания процедуры объекта-приемника после окончания перетаскивания для операторов, описывающих действия результата пертаскивания.

Private Sub Объект_DragOver([индекс As Integer,]source As Control, x As Single, y As Single, state As Integer) Происходит во время выполнения операции drag&drop, т. е. при нажатой кнопки мыши.
Индекс определяет элемент массива , если имеется массив объектов.
source -аргумент, определяющий перетаскиваемый элемент (содержимое). К нему могут добавляться методы и свойства (Sourse.Tag, Sourse.Visible и т.п.).
x, y - текущие координаты мыши на форме или объекте. Всегда измеряются в единицах объекта-приемника.
state - показывает мгновенное состояние перетаскиваемого объекта по отношению к области объекта-приемника (область цели) и принимает значения:
0 (vbEnter)= Вход (элемент управления–источник входит в область цели).
1 (vbLeave)= Выход (элемент управления–источник покидает область цели).
2 (vbOver)= Над (элемент управления источник перемещается в пределах области цели из одного положения в другое).

С помощью этого события создаются процедуры для визуализации операции перетаскивания (изменения курсора, подсвечивание объекта).

Теперь можно начинать чего-нибудь потаскать. Начнем с малого, а именно с автоматического перетаскивания.

Автоматическое перетаскивание.

Автоматическое перетаскивание - наиболее простая и наиболее худшая реализация технологии Drag&Drop.
Делается это элементарно. У многих объектов имеется свойство DragMode. Оно определяет режим перетаскивания для объекта и может принимать два значения: .DragMode=0 , т. e. вручную. и .DragMode=1 , т.е. автоматически. По умолчанию оно в проекте всегда установлено состояние 0, то бишь ручное, и это правильно. Но мы идем идиотским путем (а его нужно пройти обязательно), поэтому установим свойство DragMode=1. Это можно сделать в окне свойств, а можно программно.
Давайте создадим новый exe-прокт и посмотрим, как это все работает. Положим на форму Text1, Text2 и Text3 со свойствами Multiline=True.
Сейчас мы попытаемся организовать перетаскивание содержимого Text1 и Tex2 в Text3. И это нам удастся. Пишем код. Начнем со стандартного (хоть тут и нет переменных)

Option Explicit

В загрузке формы установим автоматический режим свойства DragMode для Text1 и Text2 и заполним их текстом, а Text3 очистим:

Private Sub Form_Load()
Text1.DragMode = 1
Text2.DragMode = 1
Text1.Text = "Это какой-то текст, записанный в Text1"
Text2.Text = "А это другой текст, записанный в Text2"
Text3.Text = ""


Да, чуть не забыл. Нам дана крутая возможность во время перетаскивания менять изображение курсора мышки. Обалдеть можно. Хотя, на самом деле, это важно. Пользователь должен контролировать состояние перетаскивания. Ну и на этом спасибо. Курсор меняется с помощью свойства .DragIcon. Это свойство определяет значок, который будет использоваться в качестве указателя мыши при операции перетаскивания. Ты можешь в окне свойств установить свойству DragIcon какую-нибудь иконку - файл с расширением .ico. Мы для ясности сделаем это программно.

Text1.DragIcon = LoadPicture(App.Path & "\Move.ico")
Text2.DragIcon = LoadPicture(App.Path & "\Move.ico")


Но при программной загрузке картинок их всегда надо таскать с проектом, а при загрузке картинок в окне свойств они становятся частью проектов и отдельно их хранить не надо.
Все. Процедура Form_Load закончена.

End Sub

Теперь логика следующая. Мы будем использовать для создания процедуры событие DragDrop, а поскольку оно возникает по окончании процесса перетаскивания, то процедуру надо создавать в Text3. Ведь именно там конечный пункт нашего процесса.

Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)

Координаты мыши X и Y нас сейчас мало интересуют, все равно VB сам определит, перетащили мы объект на Text3 или нет. А вот аргумент Source мы используем. С его помощью и ключевого слова TypeOf мы проведем проверку типа объекта (то что мы перетаскиваем Text в Text, а не Text в Picture) и присобачив к нему свойство .Text изменим содержимое Text3. Это и будет результат перетаскивания.

If TypeOf Source Is TextBox Then
Text3.Text = Source.Text
End If
End Sub


Теперь ты можешь опробовать результат, запустив проект и поперетаскивать в Text3 и Text1 и Text2. Если ты не смог написать эти пятнадцать строк кода самостоятельно, то скачать исходник примера можешь здесь.
Откровенно говоря в данном случае ключевое слово TypeOf притянуто за уши, так как других объектов на форме нет.
Можно вместо этого проверять конкретно каждый элемент и присваивать значение того, которого захотим. Для идентификации элемента есть простенькое свойство Tag. Смысл его в том, что по нему с помощью аргумента Source мы можем однозначно идентифицировать объект-источник. В отличие от других свойств, значение свойства Tag не используется языком Visual Basic, но его можно использовать для идентификации объектов. Этому свойству мы можем присвоить любое имя-идентификатор, главное, чтоб они не были одинаковыми. Для этого в процедуру Form_Load допишем две строчки:

Text1.Tag = "Text один"
Text2.Tag = "002"


Теперь наша процедура Text3_DragDrop будет выглядеть следующим образом:

Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)
If Source.Tag = "Text один" Or Source.Tag = "002" Then
Text3.Text = Source.Text
End If
End Sub


Опять же, если чего не получилось, скачать эту фигню можно здесь.

Теперь рассмотрим результаты нашей деятельности. Юзер кайфует, радостно перетаскивая текст из одного объекта в другой. Это несомненно хорошо. Потом юзеру надоедает это делать и он с удивлением обнаруживает тот факт, что отредактировать-то текст он не может ни в Text1, ни в Text2. Это несомненно плохо. А дело в том, что инициируемые юзером события мыши или клавиатуры KeyDown, KeyPress,KeyUp, MouseDown, MouseMove и MouseUp распознаваться НЕ БУДУТ!
Видя такое дело, нам целесообразно перейти к более сложному, но контролируему делу - ручному перетаскиванию.

Метод Drag - ручное перетаскивание.

Этот метод начинает, завершает или отменяет операцию перетаскивания любого из элементов управления кроме Line, Menu, Shape, Timer и CommonDialog. Он позволит нам не блокировать объект для редактирования, использовать всевозможные иконки, чтоб юзер видел, что творит при перетаскивании и вообще правильный метод.
Еще раз напоминаю очень важную вещь, если ты не понял из предыдущего: результат перетаскивания над перемещаемым объектом (копирование, удаление, вставка и т.д.) определяется в объекте-приемнике в процедуре обработки события DragDrop.
Итак, для того, чтобы начать чего-нибудь перетаскивать, надо перейти в режим перетаскивания. Для этого обычно используется процедура события MouseDown для объекта-источника. В ней мы включаем метод Drag. Для включения-выключения метода и отмены перетаскивания используются следующие константы, их объявлять не надо:
vbCancel =0 'отменяет операцию перетаскивания
vbBeginDrag =1 'включает метод Drop
vbEndDrag =2 'выключает метод Drop

Давай-ка лучше сделаем новый exe-проект и попробуем все на практике. Наш новый проект мало чем отличается от старого по внешнему виду, но не по коду. На форме у нас будут Text1, Text2, Text3 и еще кнопка Command1. Смысл проекта в том, чтобы перетаскивать содержимое Text1 в Text3, а Text2 запрещен для перетаскивания. И для прикола, потаскаем по форме командную кнопку Command1. Итак, начнем.
Объявим переменную Flag как булеву. На фига я ее ввожу? Ну не знаю, чтобы позволить юзеру беспрепятственно редактировать текст в объекте-источнике, т.е в Text1. Т.е. изначально Flag=False и при первом щелчке метод Drag в процедуре Text1_MouseDown не включается. Юзер спокойно может редактировать текст. Но в конце процедуры Flag меняет свое значение на противоположный (Flag = Not Flag) и при повторном нажатии кнопки мыши уже включается метод Drag.

Option Explicit
Dim
Flag As Boolean

В процедуре загрузки формы зададим какое-нибудь содержимое TextBox'ам (можем и не задавать и вообще наплевать на нее):

Private Sub Form_Load()
Text1.Text = "Это какой-то текст, записанный в Text1"
Text2.Text = "А я вообще не знаю, что здесь делаю"
Text3.Text = "А это другой текст, записанный в Text3"
End Sub


Далее в событии MouseDown для объекта-источника Text1 включим метод Drag для левой кнопки мыши. И меняем состояние переменной Flag:

Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
'проверяем, если нажата левая кнопка мыши и это повторное нажатие то
If Button = vbLeftButton And Flag = True Then
'включаем метод Drag
Text1.Drag vbBeginDrag
'загружаем общую иконку для курсора в виде молнии. Она будет обозначать,
'что объект готов к перетаскиванию

Text1.DragIcon = LoadPicture(App.Path & "\Move.ico")
End If
'меняем значение флага на противоположное
Flag = Not Flag
End Sub


В объекте-приемнике по событию DragDrop производим результат перетаскивания, т.е. присваиваем значение Text3=Text1. При этом проверяем тип объекта-источника:

Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single)
'проверяем, соответствует ли тип объекта-источника TextBox'у
If TypeOf Source Is TextBox Then
'если да, то
Text3.Text = Source.Text
'и для солидности передадим фокус в объект-приемник (Text3)
Text3.SetFocus
End If

End Sub


Поскольку наглядность перетаскивания для юзера чрезвычайно важна, загрузим иконки в соответствии с положением мыши для объекта-источника (Text1):

Private Sub Text1_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
'если мышь над объектом-источником грузим обычную иконку (молния)
If State = vbEnter Then
Text1.DragIcon = LoadPicture(App.Path & "\Move.ico")
'если покидает объект, то критический треугольник
ElseIf State = vbLeave Then
Text1.DragIcon = LoadPicture(App.Path & "\No.ico")
End If
End Sub


и для объекта-приемника. Причем, обратите внимание, что в процедуре Text3 объекта-приемника мы определяем иконки для объекта-источника (Text1). В противном случае возникнет путаница.

Private Sub Text3_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
'если мышь над объектом-приемником, то листик с кнопкой - готовность принять объект
If State = vbEnter Then
Text1.DragIcon = LoadPicture(App.Path & "\Yes.ico")
'если вне объекта-приемника - то критический треугольник
ElseIf State = vbLeave Then
Text1.DragIcon = LoadPicture(App.Path & "\No.ico")
End If
End Sub


Вот вобщем-то и все. В заключении я хочу показать, что использование проверки типа объекта-источника важна. На этой же форме включим метод Drag для командной кнопки Command1.

Private Sub Command1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
Command1.Drag vbBeginDrag
End If
End Sub


и напишем процедуру ее таскания по форме

Private Sub Form_DragDrop(Source As Control, X As Single, Y As Single)
If TypeOf Source Is CommandButton Then
Source.Move X, Y
End If
End Sub


Если бы мы не проверяли тип объекта с помощью ключевого слова TypeOf, то при попытке перетащить кнопку в Text3 получили бы ошибку и фатальное завершение программы. Можешь попробовать. А при наличии проверки наши методы не мешают друг другу и все проходит успешно.

В этой главе мы разобрали технологию Drag&Drop для перетаскивания в пределах одного нашего приложения. Скачать исходник примера можно вверху страницы.
В следующей главе мы попробуем исследовать эту технологию для обмена данных между разными приложениями.

Copyright © 2005 4us




Сайт создан в системе uCoz