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

Программирование для начинающих и не только


Информационный Канал Subscribe.Ru


"Межплатформенный" Drag & Drop (Часть 1. Windows)

Судя из названия можно подумать, что речь идет о перетаскивании(Drag&Drop) объектов меж платформами. Но это не так. Здесь описываются основные отличительные черты этого механизма при работе со средой Windows и Linux, а также приводятся основные методы работы с каждой из платформ.

*

Сначала рассмотрим технологию Drag&Drop в Windows т.к. большинство моих знакомых программистов работают именно в ней.

Немного Windows API

Итак, чтоб заставить приложение реагировать на события Drag & Drop нам нужно воспользоваться такими функциями: DragAcceptFiles, DragQueryFile и DragFinish из модуля ShellAPI.pas. Первая из них имеет вид:
procedure DragAcceptFiles(Wnd: HWND; Accept: BOOL); stdcall;

    Параметры:
  • Wnd
    - Дескриптор окна, для которого будет установлено разрешение на прием перетаскиваемых объектов.
  • Accept
    - собственно разрешение (True - разрешить прем объектов False - запретить прием объектов)
При установленном флаге Accept, реакция приложения распространяется на все файлы и папки расположенные на любых дисках. Реакция приложения не распространяется на мета-елементы оболочки т.е. "Панель управления", "Принтеры", "Сетевое окружение", Иконки дисков в Моем компьютере... После этого при перетаскивании файлов или папок на "допущенный" элемент приложения курсор меняет свою форму с (рис.1 на рис.2). При "отпускании" объекта на элементе - ему посылается сообщение WM_DROPFILES, которое оповещает о произошедшем событии Drag & Drop. Параметр wParam сообщения содержит идентификатор события и потребуется нам дальше.

Вторая функция используется для получения списка файлов (как мы помним перетаскивать можно несколько файлов, папок...), которые были передвинуты на наш компонент:
function DragQueryFile(Drop: HDROP; FileIndex: UINT; FileName: PChar; cb: UINT): UINT; stdcall;

    Параметры:
  • Drop
    - идентификатор, который был передан нам через сообщение WM_DROPFILES.
  • FileIndex
    - номер запрашиваемого файла
  • FileName
    - указатель на строку, которая содержит имя файла с индексом(FileIndex).
  • Cb
    - размер буфера FileName.
    При передаче параметру FileIndex значения $FFFFFFFF DragQueryFile - возвращает количество файлов, которые были перетасканы на компонент; в других случаях возвращаемое значение - количество скопированных в буфер FileName символов.

DragFinish - используется для освобождения памяти занятой при перетаскивании. Формат функции:
procedure DragFinish(Drop: HDROP); stdcall;

Алгоритм работы

Во первых мы разрешим "системное" перетаскивание на какой ни будь компонент посредством DragAcceptFiles. Далее мы каким то образом должны отследить возникновение сообщения WM_DROPFILES и записать из полученных при его отправке данных значение wParam, которое потом используем вместе с DragQueryFile для того, чтоб вывести список перетасканных файлов. Ну и под конец завершить эту процедуру освобождением памяти(DragFinish).

В первом приближении все очень просто. Трудности начинают возникать, когда мы доходим до отлавливания сообщения WM_DROPFILES. И тут есть несколько вариантов:

Создать новый класс, в котором перехватывается это сообщение. Например так:

TMyListBox = class(TListBox)
  private
   FOnDropFiles:TNotifyEvent;
   FDrop:THandle;
  protected
   procedure WMDropFiles(var Message:TMessage); message WM_DROPFILES;
  public
   property Drop:THandle read FDrop write FDrop;
   property OnDropFiles:TNotifyEvent read FOnDropFiles write FOnDropFiles;
  end;

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

В этой статье мы рассмотрим первый метод, а кто захочет "пойти другим путем", может скачать компонент ShellDragDrop

Реализация

Для демонстрации мы сначала нарисуем форму и разместим на ней кнопку. В секции private нашей формы мы разместим декларацию объекта LB типа TMyListBox и добавим метод:
procedure DoDropFiles(Sender:TObject);
Далее запишем реакцию кнопки на нажатие:


procedure TForm1.Button1Click(Sender: TObject);
begin
 LB:=TMyListBox.Create(Self);
 LB.Parent:=Self;
 LB.SetBounds(10,10,100,100);
 LB.OnDropFiles:=Self.DoDropFiles;
 DragAcceptFiles(LB.Handle,True);
end;
И формы на уничтожение:

procedure TForm1.FormDestroy(Sender: TObject);
begin
 DragAcceptFiles(LB.Handle,False);
 LB.Free;
end;

Теперь перейдем к реализации нашего вновь созданного компонента TMyListBox:


procedure TMyListBox.WMDropFiles(var Message:TMessage);
begin
 Drop:=Message.WParam;
 if Assigned(OnDropFiles) then OnDropFiles(Self);
end;
И наконец - реализация собитыя OnDropFiles:

procedure TForm1.DoDropFiles(Sender:TObject);
var CB:Integer;I,j:Integer;
    Str:Array[0..MAX_PATH] of Char;
begin
I:=DragQueryFile((Sender as TMyListBox).Drop,$FFFFFFFF,nil,cb);
(Sender as TMyListBox).Items.Add(IntToStr(I));
For j:=0 to i-1 do
 begin
  FillChar(Str,SizeOf(Str),0);
  DragQueryFile((Sender as TMyLIstBox).Drop,j,Str,MAX_PATH);
  (Sender as TMyListBox).Items.Add(Str);
 end;
end;
После запуска приложения появляется главная форма с кнопкой, по нажатии которой создается компонент LB. Теперь попробуем перетащить на него один или несколько ярлыков, которые находятся на рабочем столе. Как видим - в нашем боксе появилась требуемая информация. Полный текст программы приведен в листинге №1

// листинг 1

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMyListBox = class(TListBox) private FOnDropFiles:TNotifyEvent; FDrop:THandle; protected procedure WMDropFiles(var Message:TMessage); message WM_DROPFILES; public property Drop:THandle read FDrop write FDrop; property OnDropFiles:TNotifyEvent read FOnDropFiles write FOnDropFiles; end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormDestroy(Sender: TObject); private LB:TMyListBox; procedure DoDropFiles(Sender:TObject); { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation Uses ShellAPI; {$R *.dfm} procedure TMyListBox.WMDropFiles(var Message:TMessage); begin Drop:=Message.WParam; if Assigned(OnDropFiles) then OnDropFiles(Self); end; procedure TForm1.DoDropFiles(Sender:TObject); var CB:Integer;I,j:Integer; Str:Array[0..MAX_PATH] of Char; begin I:=DragQueryFile((Sender as TMyListBox).Drop,$FFFFFFFF,nil,cb); (Sender as TMyListBox).Items.Add(IntToStr(I)); For j:=0 to i-1 do begin FillChar(Str,SizeOf(Str),0); DragQueryFile((Sender as TMyLIstBox).Drop,j,Str,MAX_PATH); (Sender as TMyListBox).Items.Add(Str); end; end; procedure TForm1.Button1Click(Sender: TObject); begin LB:=TMyListBox.Create(Self); LB.Parent:=Self; LB.SetBounds(10,10,300,100); LB.OnDropFiles:=Self.DoDropFiles; DragAcceptFiles(LB.Handle,True); end; procedure TForm1.FormDestroy(Sender: TObject); begin DragAcceptFiles(LB.Handle,False); LB.Free; end; end.


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

В избранное