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.
PropertyTOleDragPanel.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 PropertyTOleDragPanel.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.
