Разработка многомодульных интегрированных SDI-приложений (4. Технология Drag-and-Drop)

Посмотреть архив целиком

Технология Drag-and-Drop

Реализации Drag&Drop в VCL

Drag&Drop («перетащи и положи») – это один из способов обмена данными между приложениями. Этим способом обмена пользуются многие программные приложения. Например, очень часто так перемещаются файлы и папки.

В библиотеке визуальных компонент - VCL реализована собственная версия Drag&Drop – технологии, которая обеспечивает прием и передачу любых управляющих элементов внутри одной и той же формы. При таком внутреннем использовании VCL не обращается к операционной системе, хотя для организации общения различных приложений нужно было бы использовать соответствующие функции API Windows.

Пользователь, нажав на левую кнопку мыши, может перетащить управляющий элемент (объект – источник) на другой элемент (объект-приемник). В моменты перетаскивания и отпускания генерируются события, которые должна обрабатывать программа (получателем событий является элемент, над которым находится курсор мыши). Свойство компонентов DragMode отвечает за их участие в процессе обмена данными.

DragMode = dmAutomatic - события срабатывают автоматически, программист пишет только коды, соответствующих обработчиков событий.

DragMode = dmManual - все вызовы программист должен организовать сам.

События, возникающие при перетаскивании

Событие OnDragOver возникает во время перемещения курсора мыши над объектом-приемником, а также при отпускании кнопки на этом объекте.

Параметры события:

  • Sender - объект-приемник (над ним находится курсор);

  • Source - объект-источник;

  • X,Y - координаты мыши в системе клиентской области объекта Sender;

  • State - состояние (имеет одно из трех значений dsDragEnter – курсор мыши появился над элементом; dsDragMove курсор перемещается над элементом; dsDragLeave – курсор ушел с элемента или над элементом была отпущена кнопка).

  • Accept – логическая переменная. В этой переменной обработчик должен вернуть свое решение принимать или не принимать объект Source.

Например, компонент класса Label (Name=Label1) должен принимать только компоненты класса Shape (геометрические фигуры), тогда его обработчик будет содержать проверку переносимого объекта-источника (Source):

void __fastcall TForm1::Label1DragOver(TObject *Sender, TObject *Source,

int X, int Y, TDragState State, bool &Accept)

{

Accept=Source->ClassNameIs("TShape");

}

Событие OnDragDrop возникает при отпускании левой кнопки мыши над элементом, готовым к приему. Его параметры Sender, Source, X и Y имеют тот же смысл, что и у предыдущего события. Содержимое этого обработчика зависит от того, какие функции программист разрешает выполнять пользователю путем перетаскивания. Это может быть изменение местоположения объектов, обмен информацией и т.д.

В следующем примере не происходит физического перемещения объектов; компонент Label1 получает указатель на перемещенный объект (геометрическая фигура) и считывает значения свойств (Height, Width) для вычисления периметра фигуры. Результат вычислений размещается в Label1 -> Caption.

void __fastcall TForm1::Label1DragDrop(TObject *Sender,

TObject *Source, int X, int Y)

{

float p, pi=3.14; int w, h, r, D;

if (String(Source->ClassName())=="TShape")

{

h=((TShape *)Source)->Height;

w= ((TShape *)Source)->Width ;

D = w; if ( D > h ) D = h;

switch ((( TShape *)Source) -> Shape)

{ case stRectangle:

Label2->Caption ="Прямоугольник";

p= 2*(w + h);

break;

case stSquare:

Label2->Caption = "Квадрат";

p= 4*D;

break;

case stCircle:

Label2->Caption = "Круг";

p= pi*D;

break;

case stEllipse:

Label2->Caption = "Эллипс";

p=pi*(3*(w+h)/4-sqrt(w*h)/2);

break;

case stRoundRect:

Label2->Caption = "Закругленный

прямоугольник";

r=(D-((TShape*)Source)->Pen->

Width +1)/4;

p=2*(w+h)-8*r+2*pi*r;

break;

case stRoundSquare:

Label2->Caption="Закругленный

квадрат";

r=(D-((TShape*)Source)->Pen->

Width+1)/4;

p=4*D-8*r+2*pi*r;

break;

}

p=p/(Form1->PixelsPerInch/2.54);

Label1->Caption = FloatToStr (p);

}

}

При завершении процесса перетаскивания вне зависимости от его результата исходный объект получает событие OnEndDrag. Его параметры:

  • Sender – указатель объекта - источника;

  • Target – указатель объекта - приемника;

  • X, Y – координаты курсора.

В следующем примере переносимый компонент Shape1 узнает имя объекта - приемника (если объект не принят передается NULL):

void __fastcall TForm1::Shape1EndDrag(TObject *Sender, TObject *Target,

int X, int Y)

{

if (Target != NULL)

Label2->Caption =(AnsiString) (( TComponent *) Target)->Name;

}


Идентификация типа объекта


Так как при передаче параметров объекты (Sender, Source, Target) имеют тип Tobject (базовый класс всех компонентов VCL – библиотеки), приходится определять, к какому классу в действительности принадлежит тот или иной компонент. Это можно сделать разными способами.

Приведение типа

Если необходимо рассмотреть свойство или применить к объекту метод, который объявлен в классах-наследниках, то необходимо привести тип указателя к тому классу, где это свойство или метод имеются.

Свойство Name есть у всех компонент библиотеки VCL и объявлено оно в классе TСomponent. Выражение ((TСomponent *)Sender) является приведением типа параметра Sender к типу указателя на объект класса TСomponent. Теперь можно узнать имя компонента:

Label1->Caption = ((TСomponent *)Sender) -> Name;

или

TComponent *Obj;

Obj = ((TСomponent *)Sender);

Теперь переменную Obj можно использовать как указатель на компонент:

Label1->Caption = Obj -> Name;

Часто программисту необходимо узнать является ли класс объекта потомком того класса, методы и свойства которого мы хотим использовать. Это можно проверить с помощью метода InheritsFrom, объявленного в TObject и возвращающего логический результат. Например, можно убедиться, что объект наследует свойства класса TWinControl:

if (Sender -> InheritsFrom(__classid(TWinControl)))

. . . ;

оператор __сlassid(classType) используется компилятором для генерации ссылки на указанный класс, в данном примере на класс TWinControl.

Определение истинного класса объекта

В классе TObject определен метод ClassName(), возвращающий строку, содержащую действительный класс объекта. Имя класса можно, например, увидеть на экране:

Label1 -> Caption = Sender-> ClassName();

Или программно проверить принадлежность к интересующему нас классу:

if (String (Sender -> ClassName()) == “TShape”)

. . . ;

Для такой же проверки можно использовать функцию ClassNameIs, определенную в TObject и возвращающую результат типа bool.

If (Sender -> ClassNameIs(“TShape”) )

. . . ;

Иерархия классов объекта

Метод ClassParent, определенный в TObject позволяет определить класс, являющийся непосредственным предком класса заданного объекта. Если это объект класса TObject, то есть класс не имеет предков, то вернется NULL.

Для восстановления всей иерархии классов некоторого объекта следует использовать данный метод в цикле. На рис. 2.1 представлено главное окно приложения, которое в компоненте TreeView, предназначенном для отображения иерархических данных в виде дерева, отображает иерархию классов указанного объекта. Сами объекты (Edit1, Shape1, Button1 и Image1) также расположены в этом окне, а слева от них указаны имена классов объектов.




В программе использована технология Drag-and-Drop. Пользователь “перетаскивает” объект на компонент TreeView1 и, получив указатель на объект-источник информации, метод TreeView1DragDrop в цикле считывает имена классов объекта.


void __fastcall TForm1::TreeView1DragDrop(TObject *Sender, TObject *Source,

int X, int Y)

{

// Получение ссылки на класс объекта

TClass ClassRef = Source->ClassType();

if (Sender->ClassNameIs("TTreeView"))

{

// Получение указателя на объект TreeView1

TTreeView *pTV = (TTreeView *)Sender;

// Удаление предыдущей информации из TreeView1

while (pTV->GetNodeAt(0,0)!=NULL)

pTV->Items->Delete(pTV->GetNodeAt(0,0));

}

// Имена родительских классов добавляются в первый // узел дерева

while (ClassRef!=NULL)

{

TreeView1->Items->AddFirst(TreeView1->

GetNodeAt(0,0),ClassRef->ClassName());

ClassRef = ClassRef->ClassParent();

}

Label1->Caption ="Источник: " + (AnsiString)

(( TComponent *) Source)->Name;

}


Следующий код обработчика события OnDragOver определяет будет ли принят объект-источник компонентом TreeView1.

void __fastcall TForm1::TreeView1DragOver(TObject *Sender, TObject *Source,

int X, int Y, TDragState State, bool &Accept)

{ if (Source!=Sender)

Accept=true;

else

Accept=false;

}


Обработчик события OnEndDrag исходного объекта проверяет принят ли объект каким-либо целевым объектом..


Void __fastcallTForm1::ObjectEndDrag

(TObject*Sender, TObject *Target,

int X, int Y)

{

if (Target==NULL)

Label2->Caption="Нет целевого объекта";

else

Label2->Caption ="Приемник " + (AnsiString)

(( TComponent *) Target)->Name;

}

Технология Drag&Doc

Общие сведения

В VCL библиотеке реализована также технология перетаскивания и встраивания оконных объектов (Drag&Doc).

Приемник, способный принимать переносимые компоненты (клиенты), принято называть контейнером. Если для оконного компонента установить свойство DockSite = true, то он становится контейнером. Свойство приемника UseDockManager = true определяет автоматическое встраивание клиента в приемник. Это не всегда желательно, так как размер и выравнивание компонент в приемнике тоже выбираются автоматически.

Для потенциальных клиентов следует установить свойство DragKind = dkDock. DragMode = dmAutomatic означает, что процесс перетаскивания начинается автоматически.

Для создания тестового примера следует начать новое приложение и разместить на форме один компонент-панель (Panel1) и два компонента класса Image (Image1 и Image2), загрузить в эти компоненты изображения из битовых файлов (свойство Picture). Следующие упражнения помогут разобраться в значениях свойств:

Установить для Form1 и Panel1 свойство DockSite = true; для Image1, Image2 DragKind=dkDock, а DragMode = dmAutomatic. Протестировать приложение.

Дополнительно для Panel1 установить UseDockManager = true. Протестировать приложение.



На рисунке показана картинка в состоянии плавающего окна и размещенная в компоненте Panel1. Перевести размещенный компонент в состояние плавающего окна можно и в процессе выполнения программы. Метод ManualFloat имеет единственный параметр типа TRect, который определяет положение и размещение плавающего окна.

void __fastcall TForm1::Button1Click(TObject *Sender)

{

TRect temp;


temp.Left=Form1->Left+30;

temp.Top=Form1->Top+40;;

temp.Right=temp.Left+200;

temp.Bottom=temp.Top+100;


Image2->ManualFloat(temp);

}

Следующий код плавающий объект размещает на форме:

void __fastcall TForm1::Button2Click(TObject *Sender)

{

TRect temp;

TPoint p;

p=Image2->ClientToScreen(Point(0,0));

temp.Left=p.x;

temp.Top=p.y;

p=Image2->ClientToScreen(Point(160,80));

temp.Right=p.x;

temp.Bottom=p.y;


Случайные файлы

Файл
85801.rtf
145024.rtf
d3-17.doc
23513.rtf
задача 00.doc




Чтобы не видеть здесь видео-рекламу достаточно стать зарегистрированным пользователем.
Чтобы не видеть никакую рекламу на сайте, нужно стать VIP-пользователем.
Это можно сделать совершенно бесплатно. Читайте подробности тут.