BCBで任意のコンポーネントへのドラッグ&ドロップ(Drag&Drop)を実現する


※以下の内容はメモ的内容であるため、動作については一切補償しません。従って、 このプログラムによって生じた、いかなる損害についても責任を負いかねますので予めご了承ください。

アプリケーションを作成していると、ファイルやフォルダをエクスプローラからドラッグ&ドロップ(Drag&Drop)したいという 要求が生まれます。BCBでドラック&ドロップを実現する手法としては、主に2通りの手法が考えられると思います。 (1)VCL_MESSAGE_HANDLER マクロを使用する。 (2)Application->OnMessageを使用する。 (1)の手法は簡単に実装する事ができ、ユニット単位で完結するため、見栄えの良いソースになります。 しかし、フォームへのドラッグ&ドロップ(Drag&Drop)のみで、個々のコンポーネントに対して有効ではないという問題があります(筆者が知らないだけかも知れませんが…)。 (2)の手法は、ソースが煩雑になる傾向があるものの、 個々のコンポーネントに対してドラッグ&ドロップ(Drag&Drop)を実装することができます。今回は(2)Application->OnMessageの手法で実装してみたいと思います。

実装について

ドラッグ&ドロップ(Drag&Drop)で重要な関数は、DragAcceptFilesDragQueryFileDragFinishの3つです。 これらの関数は、ウィンドウがドラッグ&ドロップを受け入れる設定をする関数、ドロップされたファイル名を取得する関数、ドロップ時にメモリを解放する関数です。 これに加えて、BCBではApplication->OnMessageを使用します。このイベントはアプリケーションにPostMessageされた全てのメッセージを処理するもので、 このイベントに任意のコンポーネントに送られるWM_DROPFILESを検出して処理を行う関数を指定する事で今回の処理を実現します。 以下にコードを示しますので参考にして下さい。なお、サンプルではTMemoを使用しているので事前にフォームに貼り付けておいて下さい。

デバッグについて

Borland C++ Builder 6をWindows Vista上で動かす場合、管理者権限の都合上、 管理者権限で操作している開発アプリに一般ユーザー権限で操作しているエクスプローラー からドラッグ&ドロップをすることはできないようです。作成したEXEファイルを直接実行してデバッグすると良いと思います。

Unit1.h

//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include >Classes.hpp<
#include >Controls.hpp<
#include >StdCtrls.hpp<
#include >Forms.hpp<
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE 管理のコンポーネント
    TMemo *Memo1;
private:    // ユーザー宣言
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);

    void __fastcall WMDropFiles( TWMDropFiles &Msg );//この宣言を追加
    void __fastcall AppMessage(tagMSG &Msg, bool &Handled);//この宣言を追加
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Unit1.cpp

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    //ドラッグで受け取り可能に(例えばMemo1を指定)
    DragAcceptFiles(Memo1->Handle, true );

    //なお、DragAcceptFilesに渡したハンドルが変更される操作をすると、
    //ドラッグ&ドロップできなくなるので注意して下さい。
    //Form1->BorderStyle = bsSingle;   //これはNG

    Application->OnMessage = AppMessage; //メッセージを処理する関数を指定
}
//---------------------------------------------------------------------------


//==============================================================================
//ドラッグ&ドロップを実現するために以下のコードを追加して下さい。
void __fastcall TForm1::WMDropFiles( TWMDropFiles &Msg )
{
    // 変数宣言
    int FileCount;              //受け取ったファイル数
    char FileName[ MAX_PATH ];  //ファイルを一時保存
    Application->BringToFront();   //アプリケーションを前面に移動

    // ファイル数を得る
    FileCount = (int)DragQueryFile( (HDROP)Msg.Drop , 0xFFFFFFFF , NULL , MAX_PATH );

    //ファイルパスを得る
    Form1->Memo1->Clear();
    for( int i=0 ; i < FileCount ; ++i ){
        DragQueryFile((HDROP)Msg.Drop, i, FileName, MAX_PATH );
        Form1->Memo1->Lines->Add(FileName);
    }
    DragFinish((HDROP)Msg.Drop);
    Msg.Result = true;//falseならば、他のコントロールでも処理される可能性があるがfalseでも良い。
}
//==============================================================================

//==============================================================================
//アプリケーションのメッセージループを操作する関数を実装します
//以下のコードはアプリケーションにPostMessageされたメッセージが全て通るので、
//十分注意してコードを記述して下さい。
void __fastcall TForm1::AppMessage(tagMSG &Msg, bool &Handled)
{
    if (Msg.message == WM_DROPFILES && Msg.hwnd == Memo1->Handle)
    {
        //MsgをTWMDropFiles構造体の形に合わせて変形する
        TWMDropFiles MsgDrop;
        MsgDrop.Drop = Msg.wParam;
        WMDropFiles(MsgDrop);
        Handled=MsgDrop.Result;
    }
    /* 他のメッセージに対しては Handled は False のままでおかれるので,他のハンドルが処理することが可能となる*/
}
//==============================================================================