juhara.com
Language : English Indonesia

Drag Drop Operation from Windows Explorer onto a Control

zamrony P Juhara
11 September 2006 16:59:00
 (3381 views)
Tutorial about how to drag and drop from Windows Explorer to a control

Introduction

Actually, this article was my post at Delphindo mailing list to answer question how to get list of filenames dragged and dropped from Windows Explorer onto a control.

If you ever use WinAmp, you know that we can add audio file into playlist by dragging and dropping files from Windows Explorer into WinAmp playlist listbox. The question is how?

Registering Window Handle

To get notified when drag-drop happen, we must register our window handle by calling DragAcceptFiles() function declared in ShellAPI.pas unit.

DragAcceptFiles(aWinHandle,FAccept);

This function expects two parameters. First parameter is handle that will receive drag-drop operation, second parameter tells whether it is registration or unregistration process. If it is true, window handle will receive notification when drag drop operation occured and if it is false, window handle will be removed from drag- drop notification list.

Handling WM_DROPFILES Message

WM_DROPFILES message will be sent to window handle registered for notification. wParam will hold handle to drag drop operation occured. We use this handle to get more information such retrieve number of files drag-dropped and also their names.

Because we use window handle, only control that have window handle is able to receive this message. WM_DROPFILES will be sent via window procedure of the corresponding window handle.

Get Number of Files Dragged Dropped

DragQueryFile() is used to get number of file drag-dropped.

total_file:=DragQueryFile(hDrop,$FFFFFFFF,nil,0);

This function needs four parameters, but to get number of file, only two parameters that is relevant. The rest is set to nil and 0. First parameter is drag drop handle, and second is file index, whioch must be set to $FFFFFFFF.

Get Length of Filename Dragged Dropped

We need to know length of filename, to be able to allocate buffer to hold filename data. To get length of filename we use same function, DragQueyFile(), but with a little bit different parameters, i.e, we set Fileindex to 0 - (total_file-1).

filename_len:=DragQueryFile(hDrop,FileIndex,nil,0);

Get Filename Dragged Dropped

To get file name, we use DragQueyFile(), Fileindex is set to 0 - (total_file-1),

DragQueryFile(hDrop,FileIndex,aFilename,filename_len);

afilename is data of type of PChar previously allocated with buffer length of filename_len.

Finishing Drag Drop Operation

We callDragFinish() to tell Windows that we are done with drag drop operation. This function will free memories allocated for filename buffer. We pass drag drop handle to this function.

Implementation

Next, we create its implementation. We encapsulate drag drop functionalities into one component that will make drag drop operation to a control easier. Its source code is as following code snippet:

{
================================
  TDragDrop v.1.0
=================================
 Komponen yg mengenkapsulasi proses
 drag drop files dari file manager
 ke window control
=================================
(c)2004 Zamrony P Juhara
=================================
http://www.juhara.com
=================================
}
unit DragDrop;

interface

uses
  SysUtils, Classes,Controls,Windows,Messages;

type
  TDragDropFilesEvent=procedure(Sender:TObject;const FileIndex,TotalFile:integer;
                             const Filename:string) of object;

  TDragDrop = class(TComponent)
  private
    OldWndProc:TWndMethod;
    FAccept: boolean;
    FOnDragDropFiles: TDragDropFilesEvent;
    FControl: TWinControl;
    procedure SetDragAccept;
    procedure WndProc(var msg:TMessage);

    procedure AcceptFiles(const hDrop:THandle);
    function GetTotalDroppedFiles(const hDrop:THandle):integer;
    function GetDroppedFilename(const hDrop:THandle;const FileIndex:integer):string;
    function GetDroppedFilenameLength(const hDrop:THandle;const FileIndex:integer):cardinal;
    procedure AcceptFinish(const hDrop:THandle);

    procedure SetAccept(const Value: boolean);
    procedure SetControl(const Value: TWinControl);
    procedure SetOnDragDropFiles(const Value: TDragDropFilesEvent);
  protected
    procedure DoDragDropFiles(const FileIndex,TotalFile:integer;
                               const Filename:string);virtual;
  public
  published
    property Control:TWinControl read FControl write SetControl;
    property Accept:boolean read FAccept write SetAccept;
    property OnDragDropFiles:TDragDropFilesEvent read FOnDragDropFiles write SetOnDragDropFiles;
  end;

procedure Register;

implementation
uses ShellAPI;

procedure Register;
begin
  RegisterComponents('juhara Components', [TDragDrop]);
end;

{ TDragDrop }

procedure TDragDrop.AcceptFiles(const hDrop:THandle);
var i,totalFile:integer;
    Filename:string;
begin
  TotalFile:=GetTotalDroppedFiles(hDrop);
  for i:=0 to TotalFile-1 do
  begin
    FileName:=GetDroppedFilename(hDrop,i);
    DoDragDropFiles(i,TotalFile,Filename);
    if Assigned(FOnDragDropFiles) then
      FOnDragDropFiles(Self,i,TotalFile,Filename);
  end;
  AcceptFinish(hDrop);
end;

procedure TDragDrop.AcceptFinish(const hDrop: THandle);
begin
  DragFinish(hDrop);
end;

procedure TDragDrop.DoDragDropFiles(const FileIndex, TotalFile: integer;
  const Filename: string);
begin
//defaultnya gak melakukan apa-apa
//override utk mengubah proses AcceptFiles
end;

function TDragDrop.GetDroppedFilename(const hDrop: THandle;
  const FileIndex: integer): string;
var afilename:PChar;
    size:cardinal;
begin
  result:='';
  size:=GetDroppedFilenameLength(hDrop,FileIndex);
  if Size<>0 then
  begin
    aFilename:=StrAlloc(Size);
    try
      DragQueryFile(hDrop,FileIndex,aFilename,Size);
      result:=aFilename;
    finally
      StrDispose(aFilename);
    end;
  end;
end;

function TDragDrop.GetDroppedFilenameLength(const hDrop: THandle;
  const FileIndex: integer): cardinal;
var size:cardinal;
begin
  size:=0;
  size:=DragQueryFile(hDrop,FileIndex,nil,size);
//tambah satu karena kita menggunakan string
  result:=size+1;
end;

function TDragDrop.GetTotalDroppedFiles(const hDrop: THandle): integer;
begin
  result:=DragQueryFile(hDrop,$FFFFFFFF,nil,0);
end;

procedure TDragDrop.SetAccept(const Value: boolean);
begin
  FAccept := Value;
//kalo runtime
  if not(csDesigning in ComponentState) then
    SetDragAccept;
end;

procedure TDragDrop.SetControl(const Value: TWinControl);
begin
  FControl := Value;
  //if runtime
  if not(csDesigning in ComponentState) then
    SetDragAccept;
end;

procedure TDragDrop.SetDragAccept;
begin
  if FControl<>nil then
  begin
    if FAccept then
    begin
      //if accept=true, subclass default window procedure
      OldWndProc:=FControl.WindowProc;
      FControl.WindowProc:=WndProc;
    end else
      FControl.WindowProc:=OldWndProc;

    DragAcceptFiles(FControl.Handle,FAccept);
  end;
end;

procedure TDragDrop.SetOnDragDropFiles(const Value: TDragDropFilesEvent);
begin
  FOnDragDropFiles := Value;
end;

procedure TDragDrop.WndProc(var msg: TMessage);
begin
  //msg.WParam berisi handle Drop struktur
  if msg.Msg=WM_DROPFILES then
  begin
    AcceptFiles(msg.WParam);
    msg.Result:=0;
  end else
    OldWndProc(msg);
end;

end.

Usage sample: Create new application. Drop TListbox from Component Palette onto the main form. Create event handler for OnCreate event. Flesh out FormCreate method to make it look like following code:

procedure TForm1.FormCreate(sender:TObject);
begin
  DragDrop1:=TDragDrop.Create(self);
  DragDrop1.OnDragDropFiles:=DragDropFiles;
  DragDrop1.Control:=ListBox1;
  DragDrop1.Accept:=true;
end;

procedure TForm1.DragDropFiles(Sender: TObject; const FileIndex,
  TotalFile: Integer; const Filename: String);
begin
  ListBox1.Items.Add(Filename);
end;

Everytime one or more files dragged and dropped from Windows Explorer onto ListBox1, then filenames will be listed in ListBox1. Click here to download source code.

Conclusion

In this article, I have explained how to use DragAcceptFiles(), DragQueryFile() and DragFinish() to handle drag drop operation from Windows Explorer onto our own control. I also explained how to implement this functionalities into a component to wrap and simplify drag drop operation.

Related Article

Do you like this article? Help this website improve by donating. Any amounts is appreciated.

Or you can help by bookmarking this page. Delicious Bookmark this on Delicious