Отправляет email-рассылки с помощью сервиса Sendsay

Borland C++ Builder - всякая всячина

  Все выпуски  

Borland C++ Builder всякая всячина (Drag & Drop - окончание)


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Приветствую всех получателей рассылки Borland C++ Builder - всякая всячина!

"...- Разве я выразил восхищение? - спросил Маг у Фагота.
- Никак нет, Мессир, вы никакого восхищения не выражали, - ответил тот.
- Так что же говорит этот человек?
- А он попросту соврал! - звучно, на весь театр сообщил клетчатый помошник и,
обратясь к Бенгальскому, прибавил: - Поздравляю вас, гражданин, соврамши!"

(М.Булгаков "Мастер и Маргаритта")

Сагодня мы продолжим тему Drag & Drop. Сразу же хочу извиниться за пару грубых ошибок, допущенных в прошлом выпуске. Во-первых, я сказал, что параметр Source, передаваемый в OnDragOver или OnDragDrop, указывает на объект, инициировавший операцию Drag & Drop. Это не совсем так, в этом выпуске мы рассмотрим еще два события, связанные с методом Drag & Drop, а именно OnStartDrag и OnEndDrag, на примере которых я поясню, где я был не прав. Во-вторых, свойство DragMode и сопутствующие ему свойства DragCursor и DragKind действительно объявлены в классе TControl, но как protected и переносятся в секцию published только в "конечных" компонентах, таких как TLabel, TEdit и т.д. В связи с этим возникает проблема управления режимом перемещения элементов, то есть вы не сможете в цикле перебрать все элементы формы (как объекты TControl) и установить их свойства DragMode в dmManual или dmAutomatic. Эти проблемы вам придется, к сожалению, решать частным порядком, действуя в каждом конкретном случае разными методами.

Итак, стобытия при операциях Drag & Drop возникают в следующем порядке

  • OnStartDrag - возникает один раз при инициации операции Drag & Drop. Получает в качестве аргументов два параметра - TObject *Sender, указатель на объект, инициировавший операцию и TDragObject *&DragObject, указатель на несуществующий пока объект, позволяющий управлять параметрами перемещения объекта, к которому была применена операция Drag & Drop. Ссылка дается на объект класса TDragObject, но обычно для управления перемещением создается объект производного класса TDragControlObject, позволяющего менять вид курсора, отображать объект при перемещениях и т.д. Так вот, если вы не создадите объект DragObject, то система создаст его сама, и в обработчике OnDragOver в качестве параметра Source вы будете получать указатель на объект, инициировавший операцию Drag & Drop, а если вы создадите такой объект (с помощью оператора new), то параметр Source будет указывать на объект DragObject для того, чтобы вы могли изменить параметры перемещения. По завершении операции Drag & Drop удалять объект TDragObject (или TDragControlObject) не нужно, система сама позаботится об этом. Вот такие пирожки с котятами, для меня лично это оказалось новостью и выявилось только при практической отладке.

  • OnDragOver - событие, возникающее при перетаскивании объекта-источника, но инициируемое объектом, над которым в этот момент "протаскивался" объект-источник. Повторяется периодически, подозреваю, что дублируется вместе с системным сообщением WM_MOUSEMOVE.

  • OnDragDrop - событие, возникающее при попытке "сбросить" объект источник на объект-приемник. Данное событие активируется объектом-приемником.

  • OnEndDrag - имеет несколько параметров: TObject *Sender - указатель на объект, инициировавший операцию Drag & Drop, TObject *Target - указатель на объект-приемник или NULL, если операция Drag & Drop не получила завершения, и int X, int Y - координаты места "падения" на объекте-приемнике. Это событие возникает при завершении операции Drag & Drop в любом случае, даже если не возникало события OnDragDrop (в этом случае второй параметр TObject *Target равен NULL).

Так вот, вернемся к перемещению объектов по форме. Я не буду ставить условие, в зависимости от которого объекты будут двигаться или стоять на месте, это можно решить 1000-ю способами, у нас все объекты будут свободно двигаться. Итак, на новую свежую форму накидайте несколько разных компонент, например: TLabel, TPanel, TImage (с какой-нибудь картинкой) и т.д. Установите их свойство DragMode в dmAutomatic, создайте для одного из них функции-обработчики событий StartDrag и DragOver. Полученные методы переименуйте в MoveStartDrag и MoveDragOver (как это делается, мы обсудили в предыдущем выпуске). Присвойте эти методы в качестве соответствующих свойств остальным компонентам и добавьте к методам следующий код:

//---------------------------------------------------------------------------
void __fastcall TForm1::MoveStartDrag(TObject *Sender, TDragObject *&DragObject)
{
    if (dynamic_cast<TControl *>(Sender))
    {
      TControl *Src= dynamic_cast<TControl *>(Sender);
      DragObject= new TDragControlObject(Src);
      StartPos= Src->ScreenToClient(Mouse->CursorPos);
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::MoveDragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept)
{
    if (dynamic_cast<TDragControlObject *>(Source))
    {
      TDragControlObject *Object= dynamic_cast<TDragControlObject *>(Source);
      TPoint Pos= ScreenToClient(Object->DragPos);
      Object->Control->Left= Pos.x - StartPos.x;
      Object->Control->Top= Pos.y - StartPos.y;
    }
    else Accept= false;
}
//---------------------------------------------------------------------------

Кроме того, в описании класса формы Form1 в секции private определите рабочую переменную TPoint StartPos. Она будет нужна для определения начального положения курсора от края перемещаемого объекта. Теперь, разберем белиберду в методе MoveStartDrag: сначала мы проверяем, является ли объект Sender, инициировавший событие OnStartDrag, объектом TControl (проверяется больше по привычке, страховки ради) и создаем для удобства прамой указатель типа TControl на этот объект. Далее, мы создаем вручную объект TDragControlObject, который наряду с разными полезными свойствами, обзор которых пока не входит в наши планы, содержит текущую позицию курсора в экранных координатах и указатель на перемещаемый объект, передаваемый конструктору. И, наконец, мы берем координаты курсора на настоящий момент, с помощью метода TControl::ScreenToClient приводим их к координатной системе объекта, инициировавшего операцию Drag & Drop и полученное смещение от края этого объекта сохраняем в переменной StartPos. Если мы не учтем этого смещения, каждый раз при активации события OnDragOver объект у нас будет перемещаться верхним левым углом к текущему положению курсора.

При обработке события OnDragOver мы проверяем (опять же ради подстраховки), является ли объект Source управляющим объектом типа TDragControlObject, и создаем для него "прямой" указатель. Далее, "вытаскиваем" из него текущую позицию курсора и приводим ее теперь уже к координатной системе формы ([Form1]::ScreenToClient). После этого мы вычитаем из полученных координат начальное смещение курсора и присваеваем полученные координаты свойствам Left и Top перемещаемого объекта, указатель на который содержится в свойстве Control управляющего объекта. Обратите внимание, что для корректной работы данного примера все объекты должны располагаться непосредственно на форме. Если вы хотите расположить их на групповых элементах (TScrollBox, TPanel и т.д.), вы должны будете отследить перенос объекта с формы на такой элемент и обратно. Еще одно замечание: поскольку перемещаемый элемент меняет свои координаты каждый раз при активации события OnDragOver, нам нет нужды обрабатывать это событие для формы, поскольку в любом случае в качестве объекта-источника и объекта-приемника будет выступать один и тот же начальный элемент.

Вот и все. Запустите полученный пример и проверьте перемещение элементов в действии.

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



С уважением, Васильев Евгений...

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog

В избранное