Перемещение информации без использования встроенных механизмов. | Перемещение информации с использованием встроенных механизмов. |
Borland C++ Builder имеет встроенные средства, которые позволяют перемещать между компонентами приложения не только файлы, но и любую информацию в компонентах, которую возможно выделить мышкой. Кроме того легко реализовать программно процесс перетаскивания и без использования встроенной технологии Drag&Drop. Для того, чтобы понять суть происходящих при перемещении процессов, рассмотрим сначала перемещение информации без использования встроенных механизмов.
Поместим на форму компоненты TLabel и TListbox и определим глабально или в секции private файла определения переменные vasInf типа AnsiString и viStep типа int. В обработчике события OnMouseDown компоненты Label напишем код:
void __fastcall TForm1::Label1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if(Button !=mbLeft) return; viStep=1; Screen->Cursor=crNo; vasInf=Label3->Caption; }
Здесь при нажатии левой кнопки мыши переменная vasInf принимает значение содержимого Caption компоненты TLabel и для всего окна Windopws изменяется вид курсора. Изменить курсор обычным образом (Form1->Cursor=..) нельзя, так как в этом случае он будет отображен только после отпускания кнопки мышки.
Далее предусматриваем, возможность отказа от перетаскивания.
void __fastcall TForm1::Label1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if(Button !=mbLeft) return; if(viStep == 1) viStep=0; Screen->Cursor=crDefault; }
Данный код при отпускании левой кнопки мышки всегда позволяет вернуть курсор к начальному значению и если этап перетаскивания соответствует начальному, возвращает все в исходное состояние.
Рассмотрим далее процесс перетаскивания. Суть следующего кода в том, что при начале и в период перетаскивания идет установка этапа 2 и при попадании курсора в область поля компонента TListBox вид курсора меняется на crDrag, а при выходе из области поля вновь crNo и происходит возврат к первому этапу - старту перетаскивания.
void __fastcall TForm1::Label1MouseMove (TObject *Sender, TShiftState Shift, int X, int Y) { if(viStep == 1) viStep=2; if(viStep == 2) { TPoint cPt; //Позиция курсора GetCursorPos(&cPt); X1=(int)(cPt.x);// Возвращает y-позицию курсора мыши Y1=(int)(cPt.y);// Возвращает x-позицию курсора мыши if(X1 > Left+ListBox1->Left+20 && X1 < Left+ListBox1->Left+ListBox1->Width-20 && Y1 > Top+ListBox1->Top+40 && Y1 < Top+ListBox1->Top+ListBox1->Height-40) { Screen->Cursor=crDrag; }else { viStep=1; Screen->Cursor=crNo; } } }
На данном этапе viStep равен 1 если идет перетаскивание и мышка не в пределах компоненты TListBox и 2 если в пределах. Если viStep равен 2, то при отпускании мышки его значение не изменится, хотя курсор и вернется в исходное состояние, а так как мышка находится в поле TListBox и хотябы от дрожания руки немного сдвинется, то сработает следующий код:
void __fastcall TForm1::ListBox1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if(viStep == 2) { viStep=0; ListBox1->Items->Add(vasInf); Screen->Cursor=crDefault; } }
Информация из Caption компонента TLabel передана в компонент TListBox.
Для того, чтобы обеспечить процесс перемещения информации в пределах приложений многие компоненты включают в себя соответствующие свойства и методы.
Среди свойств, предназначенных для этой цели, свойство DragMode определяет начало процесса перетаскивания. Значение dmManual означает, что возможность начать процесс перетаскивания определяет программист, dmAutomatic - процесс перетаскивания начинается сразу по нажатии в поле компонента левой кнопочки мышки.
Разместим на форме 3 компонента (рис 1): Memo1, ListBox1 и TButton И поставим задачей переместить текст, написанный в TМемо в компонент ListBox.
Установим свойство DragMode для компонента Memo1 dmAutomatic. А чтобы показать, что компонент TListBox может принимать информацию, создадим для него обработчик события OnDragOver, которое наступает если над компонентом движется курсор в режиме перемещения информации из другого компонента или некоторого компонента.
void __fastcall TForm1::Memo1DragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept) { Accept=true; }
Здесь может быть предусмотрено множество проверок по положению курсора или по уточнению источника (Source) откуда перемещается информация. Задача - если переменную Accept установить в положение true, то компонент примет брошенную над ним информацию и нет в противном случае. Кроме того Accept рпределяет и смену вида курсора с crNo на crGrag.
Осталось принять информацию. Когда над компонентом при выполнении всех вышеперечисленных условий будет отпущена кнопочка мышки, то в компоненте наступает событие OnDragDrop. В обработчике этого события и записывается код приема информации.
void __fastcall TForm1::ListBox1DragDrop(TObject *Sender, TObject *Source, int X, int Y) { TMemo *tmemo =(TMemo*)Source; //Принять все for(int i=0; i < tmemo->Lines->Count; i++) { ListBox1->Items->Add(tmemo->Lines->Strings[i]); } }
Этот пример показывает и достоинства и недостаток работы в режиме DragMode = dmAutomatic. Удобно, что код без труда можно перебрасывать между компонентами, но при попытки выделить текст мышкой, курсор сразу приобретает вид crDrag и перенести можно только все или ничего, да и событие OnMouseDown для данного компонента в этом режиме вообще не возникает.
Для того, чтобы обойти указанный недостаток DragMode присвоим значение dmManual и создадим обработчик события OnClick для TButton.
void __fastcall TForm1::Button1Click(TObject *Sender) { Memo1->BeginDrag(false,5); Memo1->SetFocus(); }
Функция BeginDrag переводит компонент в режим перемещения информации (параметр false означает отсрочку процесса начала перетаскмвания до момента, когда курсор сдвинется не менее чем на указанное число пикселей от исходной точки.
Если теперь в обработчике события ListBox1DragDrop заменить код на строку
ListBox1->Items->Add(Memo1->SelText);
то можно выделить кусок текста в компоненте TMemo, а затем нажав на кнопку Button1, перетащить только выделенный текст в поле компонента TListBox.
Рассмотрим два аспекта перемещения файлов
Перемещение имен файлов. | Перемещение информации из файлов. |
Разместим на форме 4 компонента (рис 1): TDriveComboBox, TDirectoryListBox, TFileListBox из вкладки Win3.1 и TListBox из вкладки Standart и поставим задачей выделять файлы в элементе TFileListBox и сначала отобразить имена файлов в компоненте TListBox, а затем и содержимое файлов.
Свойство DirList компонента TDriveComboBox установим в DirectoryListBox1, свойство FileList компонента TDirectoryListBox1 в FileListBox1 и таким образом свяжем компоненты, что позволит отображать список файлов на выбранном диске в выбранной директории. Свойству MultiSelect для TFileListBox присвоим значение true - это позволить выбирать в TFileListBox несколько имен файлов одновременно.
Установим свойство DragMode для компонента TFileListBox dmAutomatic. Чтобы показать, что компонент TListBox может принимать информацию, создадим для него обработчик события OnDragOver, которое наступает если над компонентом движется курсор в режиме перемещения информации из другого компонента.
void __fastcall TForm1::ListBox1DragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept) { Accept=true; }
Здесь, как описывалось ранее, также может быть предусмотрено множество проверок по положению курсора или по уточнению источника (Source) откуда перемещается информация. Например проверка, приведенная ниже, позволит принимать в TListBox имена файлов только из компонент TFileListBox:
if(Source->ClassNameIs("TFileListBox")) Accept=true; else Accept=false;
Идея прежняя - если переменную Accept установить в положение true, то компонент примет брошенную над ним информацию и эта переменная определяет смену вида курсора с crNo на crGrag над данным компонентом.
Для приема информации в обработчике события OnDragDrop запишем код для приема имени файла.
void __fastcall TForm1::ListBox1DragDrop(TObject *Sender, TObject *Source, int X, int Y) { ListBox1->Clear(); FileListBox1->CopySelection(ListBox1); }
Если необходимо добавлять имена файлов из других директорий, то мажно организовать пересылку через другой TListBox (к сожалению функции AddSelection у TFileListBox нет).
Как видно буксировка имен файлов в данном случае ничем не отличается от буксировки текстовой информации. Аналогично буксировки текстовой информации можно организовать и буксировку имен файлов без использования встроенных механизмов Drag&Drop.
Заметим, что здесь также можно использовать свойство DragMode компоненты TFileListBox со значением dmManual для управления процессом начала буксировки, однако наличие свойства MultiSelect делает это практически не нужным. Если же по каким либо причинам требуется запрещать и разрешать буксировку в определенных точках программы, то в этом случае следует поступать аналогично примеру, описанному выше, а именно (например добавив компонент TButton при DragMode равно false): void __fastcall TForm1::Button1Click(TObject *Sender) { FileListBox1->BeginDrag(false,5); FileListBox1->SetFocus(); }
Для добавления полных имен файлов их необходимо сформировать, например так:
AnsiString vasDir=DirectoryListBox1->Directory; if(vasDir.Length() <= 3) ListBox1->Items->Add(vasDir+ListBox2->Items->Strings[i]); else ListBox1->Items->Add(vasDir+"\\"+ListBox2->Items->Strings[i]);
Зная имя файла не составляет большого труда выполнить с ним любое действие, например загрузить файл в TListBox.
void __fastcall TForm1::ListBox1DragDrop(TObject *Sender, TObject *Source, int X, int Y) { ListBox1->Clear(); ListBox1->Items->LoadFromFile (FileListBox1->Items->Strings[0]); }
В данном случае целесообразно установить свойство MultiSelect TFileListBox в false и открытие файлов по одному. При необходимости открытия по несколько файлов можно организовать транзит через дополнительные компоненты TListBox.
void __fastcall TForm1::ListBox1DragDrop(TObject *Sender, TObject *Source, int X, int Y) { ListBox1->Clear(); ListBox2->Clear(); //Имена файлов в ListBox2 FileListBox1->CopySelection(ListBox2); for(int i=0;i < ListBox2->Items->Count; i++) { ListBox3->Clear(); //Содержимое очередного файла (его имя в ListBox2) в ListBox3 ListBox3->Items->LoadFromFile (ListBox2->Items->Strings[i]); //Перенос содержимого файла из ListBox3 в ListBox1 for(int j=0; j < ListBox3->Items->Count; j++) ListBox1->Items->Add(ListBox3->Items->Strings[j]); } }
Зная имя файла не трудно выполнить и его другие загрузки. Например код:
void __fastcall TForm1::Image1DragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept) { Accept=true; } void __fastcall TForm1::Image1DragDrop(TObject *Sender, TObject *Source, int X, int Y) { if(ExtractFileExt(FileListBox1->Items->Strings[0]) == ".bmp" || ExtractFileExt(FileListBox1->Items->Strings[0]) == ".BMP") { Image1->Picture-> LoadFromFile(FileListBox1->Items->Strings[0]); } }
По аналогии с этим примером можно открывать любые файлы в приложении.