Перемещение информации между компонентами приложения

Буксировка данных в приложениях

В начало

Перемещение информации между компонентами приложения

Перемещение информации без использования встроенных механизмов. Перемещение информации с использованием встроенных механизмов.

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.

drag1.jpg

Установим свойство 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, а затем и содержимое файлов.

drag2.jpg

Свойство 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]);
 }
}

По аналогии с этим примером можно открывать любые файлы в приложении.

В начало

На главную подраздела о буксировке

Домой


Сайт управляется системой uCoz