トップへ(mam-mam.net/)

How to Drag and Drop Files in Delphi VCL — TOleDragPanel and DoDragDrop Implementation Examples

Japanese

Dragging and Dropping Files from an Application Window (VCL)

In Delphi VCL, you can implement file drag‑and‑drop from your application to Windows Explorer by using the Windows API function DoDragDrop.
This page explains how to create a component that acts as a drag source by deriving a custom TOleDragPanel class from TPanel.
It includes practical examples of integrating Windows APIs such as ILCreateFromPath for identifying files, QueryContinueDrag for determining whether to continue a drag operation, and GiveFeedback for controlling the cursor during dragging.
If you want to add file drag‑and‑drop functionality to your Delphi applications, this guide will be a useful reference.
The source code for the TOleDragPanel class is provided at the bottom of this page. Copy and paste it into a new file named UOleDragPanel.pas and include it in your project.

Important Properties of the TOleDragPanel Class
Property
TOleDragPanel.FileName: String
Specifies the full path of the file to be used as the drag source when dropping onto Explorer or other applications.
For example, if you set it to 'c:\abc.bmp', you must either create the file in advance or generate it dynamically in the OnDrop event.
Event Property
TOleDragPanel.OnDrop: TNotifyEvent
Triggered immediately after the file is dropped onto Explorer or another target.
If the file specified in TOleDragPanel.FileName does not exist, you must generate it dynamically within this event.

Creating the Project

Start the Delphi IDE and select “File” → “Windows VCL Application – Delphi”.
From “File” → “Save All”, create a folder for the project and save both the unit and the project files.
Save the UOleDragPanel.pas file (provided at the bottom of this page) into the same folder as your project and unit files.
In the Object Inspector, open the “Events” tab and double‑click the empty space next to “OnCreate”, then copy and paste the source code shown below.

Writing the Source Code

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,  Vcl.ExtCtrls,
  Vcl.Imaging.pngimage, UOleDragPanel;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure OnDrop(Sender:TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var DragPanel:TOleDragPanel;
    img:TImage;
    png:TPngImage;
begin
  //Create a drag-and-drop source object for "test.png"
  DragPanel:=TOleDragPanel.Create(self);
  DragPanel.Parent:=self;
  DragPanel.Caption:='';
  DragPanel.FileName:=ExtractFilePath(Application.ExeName)+'test.png';
  DragPanel.Width:=160;
  DragPanel.Height:=160;
  DragPanel.Left:=0;
  DragPanel.Top:=0;
  img:=TImage.Create(self);
  img.Parent:=DragPanel;
  img.Align:=alClient;
  img.Width:=160;
  img.Height:=160;
  img.Stretch:=True;
  img.Proportional:=True;
  img.Picture.Bitmap.Width:=160;
  img.Picture.Bitmap.Height:=160;
  img.Picture.Bitmap.Canvas.TextOut(10,0,'This will be saved as "test.png"');
  img.Picture.Bitmap.Canvas.TextOut(10,20,'You can drag and drop it');
  img.Picture.Bitmap.Canvas.TextOut(10,40,'onto the Desktop or Explorer.');
  Img.Picture.Bitmap.Canvas.MoveTo(20,60);
  Img.Picture.Bitmap.Canvas.LineTo(180,200);
  //Generate the "test.png" file in advance
  png:=TPngImage.Create;
  try
    png.Assign(Img.Picture.Bitmap);
    png.SaveToFile(ExtractFilePath(Application.ExeName)+'test.png');
  finally
    png.Free;
  end;

  //Create a drag-and-drop source object for "test.csv"
  DragPanel:=TOleDragPanel.Create(self);
  DragPanel.Parent:=self;
  DragPanel.Caption:=
    'You can drag and drop this as a "test.csv" file '+
    'onto Explorer or other applications.';
  DragPanel.FileName:=ExtractFilePath(Application.ExeName)+'test.csv';
  DragPanel.Width:=340;
  DragPanel.Height:=80;
  DragPanel.Left:=0;
  DragPanel.Top:=161;
  //When dropped onto Explorer, call the OnDrop method to generate "test.csv"
  DragPanel.OnDrop:=Self.OnDrop;
end;


procedure TForm1.OnDrop(Sender: TObject);
var stl:TStringList;
begin
  //This method is called immediately after the drop, so generate "test.csv" here
  stl:=TStringList.Create;
  try
    stl.Add('a,b,c,d');
    stl.Add('1,2,3,4');
    stl.Add('5,6,7,8');
    stl.SaveToFile(TOleDragPanel(Sender).FileName);
  finally
    stl.Free;
  end;
end;

end.

Running the Application

When you drag and drop the upper TOleDragPanel onto the desktop or Windows Explorer, the file test.png will be copied.
When you drag and drop the lower TOleDragPanel onto the desktop or Windows Explorer, the file test.csv will be copied.

「UOleDragPanel.pas」ファイル

Save the following source code as UOleDragPanel.pas and place it in your project folder.

unit UOleDragPanel;

interface

uses Winapi.Windows, Winapi.Messages, System.SysUtils,System.Classes,
     Vcl.Controls,Vcl.ExtCtrls,
     Winapi.Activex,Winapi.ShlObj;

type
  [ComponentPlatformsAttribute(pidWin32 or pidWin64)]
  TOleDragPanel=class(TPanel,IDropSource)
  private
    FFileName:String;
    FThreshHold:Integer;
    FIsDragging:boolean;
    FMouseDownPoint:TPoint;
    DataObject:IDataObject;
    FOnDrop:TNotifyEvent;
    FEffects:Integer;
    //Stores the original window procedure
    FOldWndProc:TWndMethod;
    //Subclassed window procedure
    procedure NewWndProc(var msg:TMessage);
  protected
    function getDataObject(Path:String):IDataObject;
    function QueryContinueDrag(fEscapePressed: BOOL;
      grfKeyState: Longint): HRESULT; stdcall;
    function GiveFeedback(dwEffect: Longint): HRESULT; stdcall;

    procedure SetFileName(Value: string);
    function GetFileName: string;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy();
  published
    property FileName: string read GetFileName write SetFileName;
    property OnDrop:TNotifyEvent read FOnDrop write FOnDrop;
    // Specifies whether dropping allows move (DROPEFFECT_MOVE),
    // copy (DROPEFFECT_COPY), or both (DROPEFFECT_MOVE + DROPEFFECT_COPY)
    property Effects:Integer read FEffects write FEffects;
  End;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TOleDragPanel]);
end;

{ TOleDragPanel }

//Constructor
constructor TOleDragPanel.Create(AOwner: TComponent);
begin
  inherited;
  FIsDragging:=False;
  FMouseDownPoint.X:=0;
  FMouseDownPoint.Y:=0;
  FThreshHold:=4;
  FOnDrop:=nil;
  //Default drop effect: copy
  FEffects:=DROPEFFECT_COPY;
  //Subclass the window procedure
  FOldWndProc:=self.WindowProc;
  self.WindowProc:=NewWndProc;
end;

//Destructor
destructor TOleDragPanel.Destroy;
begin
  //Restore the original window procedure
  self.WindowProc:=FOldWndProc;
  inherited;
end;

//Retrieve an IDataObject from a full file path
function TOleDragPanel.getDataObject(Path: String): IDataObject;
var SFolder:IShellFolder;
    hRes:HRESULT;
    pidl:PItemIDList;
begin
  result:=nil;

  //Get the desktop folder's IShellFolder interface
  hRes:=SHGetDesktopFolder(SFolder);
  if hRes<>NOERROR then exit;

  //Obtain an ItemIDList using ILCreateFromPath (fails if file does not exist)
  pidl:=ILCreateFromPath(PChar(path));
  if pidl=nil then
  begin
    //SHSimpleIDListFromPath works even if the file does not exist
    pidl:=PItemIDList(SHSimpleIDListFromPath(PChar(path)));
    if pidl=nil then exit;
  end;
  //Get IDataObject from the ItemIDList
  hres:=SFolder.GetUIObjectOf(0,1,pidl,IDataObject,nil,DataObject);
  //Free memory
  ILFree(PItemIDList(pidl));

  if hRes=NOERROR then
    result:=DataObject;
end;

//Return the file name
function TOleDragPanel.GetFileName: string;
begin
  Result:=FFilename;
end;

//Set the file name
procedure TOleDragPanel.SetFileName(Value: string);
begin
  if Value<>FFilename then FFilename := Trim(Value);
end;

//Provide visual feedback during dragging
function TOleDragPanel.GiveFeedback(dwEffect: Longint): HRESULT;
begin
  //Use the default mouse cursor
  Result := DRAGDROP_S_USEDEFAULTCURSORS;
end;

//Subclassed window procedure
procedure TOleDragPanel.NewWndProc(var msg:TMessage);
var dwEffect:Integer;
begin
  if (msg.Msg=WM_LBUTTONDOWN) and
     (FIsDragging=False) and (FFileName<>'') then
  begin
    //Left mouse button pressed
    FMouseDownPoint.X:=TWMMouse(msg).Pos.x;
    FMouseDownPoint.Y:=TWMMouse(msg).Pos.y;
    FIsDragging:=True;
  end
  else if (msg.Msg=WM_MOUSEMOVE) and FIsDragging and
          (
            (Abs(FMouseDownPoint.X-TWMMouse(msg).Pos.x)>FThreshHold) or
            (Abs(FMouseDownPoint.Y-TWMMouse(msg).Pos.y)>FThreshHold)
          ) then
  begin
    //Mouse moved beyond threshold after pressing left button
    DataObject:=getDataObject(FFileName);
    if assigned(DataObject) then
    begin
      //Start the drag-and-drop operation
      DoDragDrop(
        DataObject,  //IDataObject
        self,        //IDropSource
        FEffects,    //Allowed drop effects
        dwEffect     //Receives final effect after DoDragDrop returns
      );
    end;
  end
  else if msg.Msg=WM_LBUTTONUP then
  begin
    //Left mouse button released
    FIsDragging:=False;
  end;
  //Call the original window procedure
  FOldWndProc(msg);
end;

//Behavior during a drag operation
function TOleDragPanel.QueryContinueDrag(fEscapePressed: BOOL;
  grfKeyState: Longint): HRESULT;
begin
  //Cancel if ESC pressed or right mouse button pressed
  if fEscapePressed or ((grfKeyState and MK_RBUTTON) = MK_RBUTTON) then
  begin
    Result := DRAGDROP_S_CANCEL;
    FIsDragging:=False;
  end
  else if (grfKeyState and MK_LBUTTON) = 0 then
  begin
    //Drop executed when left mouse button is released
    //Call OnDrop event if assigned
    if assigned(FOnDrop) then FOnDrop(Self);
    Result := DRAGDROP_S_DROP;
    FIsDragging:=False;
  end
  else
  begin
    //Continue dragging
    Result := S_OK;
  end;
end;

initialization
  //Initialize COM library
  OleInitialize(nil);

finalization
  //Uninitialize COM and release resources
  OleUninitialize;

end.