アプリのウィンドウからファイルをドラッグ&ドロップ(VCL)
Delphi VCLでは、Windows APIのDoDragDropを使って、アプリケーションからファイルをドラッグ&ドロップする機能を実装できます。
本ページでは、TPanelを継承したTOleDragPanelクラスを使い、ファイルのドラッグ元として機能するコンポーネントを作成する方法を紹介します。
ILCreateFromPathによるファイルの識別、QueryContinueDragによるドラッグ継続判定、GiveFeedbackによるカーソル制御など、実務で役立つAPI連携の実装例を掲載しています。
Delphiでファイルのドラッグ&ドロップ機能を追加したい方は、ぜひ参考にしてください。
TOleDragPanelクラス のソースコードは本ページ下部にありますのでコピー&ペーストして「UOleDragPanel.pas」ファイルとして保存して使います。
|
プロパティ TOleDragPanel.FileName: String |
エクスプローラーなどにドロップ時のドラッグ元のファイル名(フルパス)を設定します。 例えば、 'c:\abc.bmp' を設定した場合は、 'c:\abc.bmp' ファイルを事前に生成しておくか、OnDropイベントで 'c:\abc.bmp' ファイルを動的に生成しなければなりません。 |
|
イベント プロパティ TOleDragPanel.OnDrop: TNotifyEvent |
ファイルをエクスプローラー等にドロップした直後に発生するイベント。 TOleDragPanel.FileNameで設定したファイルが存在しない場合は、このイベント内で動的に生成する必要があります。 |
プロジェクトの作成
Delphi IDEを起動し、「ファイル」⇒「Windows VCLアプリケーション -Delphi」をクリックします。
「ファイル」⇒「すべて保存」からプロジェクト保存用フォルダを作成してユニットとプロジェクトを保存します。
プロジェクトやユニットを保存したフォルダに本ページの下に記載の「UOleDragPanel.pas」ファイルを保存します。
[オブジェクトインスペクタ]の[イベント]タブをクリックし、[OnCreate]の右側の何もない場所をダブルクリックして以下のソースコードをコピー&ペースト記述します。
ソースコードの記述
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 宣言 }
procedure OnDrop(Sender:TObject);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var DragPanel:TOleDragPanel;
img:TImage;
png:TPngImage;
begin
//「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,'「test.png」として');
img.Picture.Bitmap.Canvas.TextOut(10,20,'デスクトップ等に');
img.Picture.Bitmap.Canvas.TextOut(10,40,'ドラッグ&ドロップできます');
Img.Picture.Bitmap.Canvas.MoveTo(20,60);
Img.Picture.Bitmap.Canvas.LineTo(180,200);
//事前に「test.png」ファイルを生成しておく
png:=TPngImage.Create;
try
png.Assign(Img.Picture.Bitmap);
png.SaveToFile(ExtractFilePath(Application.ExeName)+'test.png');
finally
png.Free;
end;
//「test.csv」ファイルのドラッグ&ドロップ用オブジェクトの生成
DragPanel:=TOleDragPanel.Create(self);
DragPanel.Parent:=self;
DragPanel.Caption:=
'test.csvファイルとしてエクスプローラー等に'+
'ドラッグ&ドロップできます';
DragPanel.FileName:=ExtractFilePath(Application.ExeName)+'test.csv';
DragPanel.Width:=340;
DragPanel.Height:=80;
DragPanel.Left:=0;
DragPanel.Top:=161;
//「test.csv」ファイルはエクスプローラーなどにドロップされたタイミングでOnDropメソッドを呼び出す
DragPanel.OnDrop:=Self.OnDrop;
end;
procedure TForm1.OnDrop(Sender: TObject);
var stl:TStringList;
begin
//ドロップ直後に呼び出されるので「test.csv」ファイルを生成する
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.
実行する
上側のTOleDragPanelをデスクトップやエクスプローラーにドラッグ&ドロップすると「test.png」ファイルがコピーされます。
下側のTOleDragPanelをデスクトップやエクスプローラーにドラッグ&ドロップすると「test.csv」ファイルがコピーされます。
「UOleDragPanel.pas」ファイル
以下ソースコードを「UOleDragPanel.pas」ファイル名として保存し、プロジェクトフォルダに保存して使用します。
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
{ Private 宣言 }
FFileName:String;
FThreshHold:Integer;
FIsDragging:boolean;
FMouseDownPoint:TPoint;
DataObject:IDataObject;
FOnDrop:TNotifyEvent;
FEffects:Integer;
//元々のウィンドウプロシージャを保存する変数
FOldWndProc:TWndMethod;
//置き換えるウィンドウプロシージャ
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;
//ドロップ時に移動(DROPEFFECT_MOVE)又はコピー(DROPEFFECT_COPY)
//又は両方(DROPEFFECT_MOVE + DROPEFFECT_COPY)を許可するか設定
property Effects:Integer read FEffects write FEffects;
End;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TOleDragPanel]);
end;
{ TOleDragPanel }
//■コンストラクタ
constructor TOleDragPanel.Create(AOwner: TComponent);
begin
inherited;
FIsDragging:=False;
FMouseDownPoint.X:=0;
FMouseDownPoint.Y:=0;
FThreshHold:=4;
FOnDrop:=nil;
//ドロップ時に移動(DROPEFFECT_MOVE)又はコピー(DROPEFFECT_COPY)
//又は両方(DROPEFFECT_MOVE + DROPEFFECT_COPY)を許可するか設定
FEffects:=DROPEFFECT_COPY;
//ウィンドウプロシージャ置き換え(サブクラス化)
FOldWndProc:=self.WindowProc;
self.WindowProc:=NewWndProc;
end;
//■デストラクタ
destructor TOleDragPanel.Destroy;
begin
//ウィンドウプロシージャを戻す
self.WindowProc:=FOldWndProc;
inherited;
end;
//■フルパスのファイル名からIDataObjectを取得する
function TOleDragPanel.getDataObject(Path: String): IDataObject;
var SFolder:IShellFolder;
hRes:HRESULT;
pidl:PItemIDList;
begin
result:=nil;
//デスクトップフォルダのSFolder:IShellFolderを取得する
hRes:=SHGetDesktopFolder(SFolder);
if hRes<>NOERROR then exit;
//ILCreateFromPathでItemIDListを取得(ファイルが存在しないとNG)
pidl:=ILCreateFromPath(PChar(path));
if pidl=nil then
begin
//SHSimpleIDListFromPathでItemIDListを取得(ファイルが存在しなくてもOK)
pidl:=PItemIDList(SHSimpleIDListFromPath(PChar(path)));
if pidl=nil then exit;
end;
//ItemIDListからDataObject取得
hres:=SFolder.GetUIObjectOf(0,1,pidl,IDataObject,nil,DataObject);
//メモリを解放
ILFree(PItemIDList(pidl));
if hRes=NOERROR then
result:=DataObject; //IDataObjectを返す
end;
//■ファイル名を返す
function TOleDragPanel.GetFileName: string;
begin
Result:=FFilename;
end;
//■ファイル名を設定
procedure TOleDragPanel.SetFileName(Value: string);
begin
if Value<>FFilename then FFilename := Trim(Value);
end;
//■ファイルドラッグ中にユーザーに視覚的なフィードバックを提供
function TOleDragPanel.GiveFeedback(dwEffect: Longint): HRESULT;
begin
//標準のマウスカーソルを返す
Result := DRAGDROP_S_USEDEFAULTCURSORS;
end;
//■サブクラス化したウィンドウプロシージャ
procedure TOleDragPanel.NewWndProc(var msg:TMessage);
var dwEffect:Integer;
begin
if (msg.Msg=WM_LBUTTONDOWN) and
(FIsDragging=False) and (FFileName<>'') then
begin
//マウスの左ボタンが押された時
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
//マウスの左ボタンが押された後にThreshHold以上移動した時
DataObject:=getDataObject(FFileName);
if assigned(DataObject) then
begin
//ファイルのドラッグ処理を開始
DoDragDrop(
DataObject, //IDataObjectを渡す
self, //IDropSourceを渡す
FEffects, //ドロップ時にコピーや移動を許可する
dwEffect //DoDrawDropが制御を返した時の最後のエフェクトを受取る
);
end;
end
else if msg.Msg=WM_LBUTTONUP then
begin
//マウスの左ボタンが離された時
FIsDragging:=False;
end;
//元のウィンドウプロシージャを呼ぶ
FOldWndProc(msg);
end;
//■ファイルドラッグ中の動作
function TOleDragPanel.QueryContinueDrag(fEscapePressed: BOOL;
grfKeyState: Longint): HRESULT;
begin
//「Esc」キーが押されたか、マウス右ボタンが押された場合
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
//●ドロップ実行された場合
//OnDropプロパティが設定されていたら呼び出す
if assigned(FOnDrop) then FOnDrop(Self);
//ドロップ実行
Result := DRAGDROP_S_DROP;
FIsDragging:=False;
end
else
begin
//●ドラッグ中の場合
//ドラッグ操作を続行
Result := S_OK;
end;
end;
initialization
//COMライブラリを初期化
OleInitialize(nil);
finalization
//COMライブラリを閉じリソースを解放する
OleUninitialize;
end.
