ドラッグ&ドロップを受け入れるTMemoとTImage


ここに書いたようにすれば フォームがドラッグ&ドロップを受け入れられるようになる. しかし,例えば,ドロップしたファイルをMemoコンポーネントに読み込ませるような場合は, Memoコンポーネントのみがドラッグ&ドロップを受け入れるようにした方がよい. ここでは,上で書いたサブクラス化によってこれを実現してみる.

コードは以下のようになる.

var
  OrgProc: Pointer;

function NewProc( hwnd: HWND; iMsg: UINT;
                  wParam: WPARAM; lParam: LPARAM ): LRESULT; stdcall;
var
  DroppedFile: array[ 0..255 ] of Char;
begin
   case iMsg of
    WM_DROPFILES:
    begin
       DragQueryFile( wParam, 0, DroppedFile, SizeOf( DroppedFile ) );
       Form1.Memo1.Lines.LoadFromFile( DroppedFile );
       DragFinish( wParam );
    end;
   end;
   Result := CallWindowProc( OrgProc, hwnd, iMsg, wParam, lParam );
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   DragAcceptFiles( Memo1.Handle, True );
   OrgProc := Pointer( SetWindowLong( Memo1.Handle, GWL_WNDPROC,
                       DWORD( @NewProc ) ) );
end;

置き換えるウィンドウプロシージャ(WP)ではWM_DROPFILESメッセージに応答できる ようにする. このメッセージではwParamがドロップ構造体のハンドルになっているので, これをDragQueryFileの第一引数として渡せばよい. 当然,DragAcceptFilesにはMemoコンポーネントのハンドルを渡す.

次に,Imageコンポーネントもドラッグ&ドロップを受け入れられるようにしてみよう. ただし,Memoと違ってImageコンポーネントはウィンドウハンドルを持たないので, DragAcceptFilesにハンドルを渡してWM_DROPFILESメッセージを送ってもらうことができない. Imageを継承したコンポーネントを作るなら,Timerコンポーネントなどで 使われているAllocateHWND関数でウィンドウハンドルを取得してもよいが, ここはもっと簡単な方法を使うことにする.

ImageをPanelに貼り付けてPanelのウィンドウハンドルをDragAcceptFilesに渡すのである. PanelはTWinControlから派生しているから,ウィンドウハンドルもWPも持っており, 上と全く同じ方法でサブクラス化することができる.

ただ,それでは面白くないので,TControl以下が持っているWindowProcプロパティを 使ってみよう. このプロパティはVCLのWPへのメソッドポインタであり, 以下のように宣言されている.

type TWndMethod = procedure( var Message: TMessage ) of object;
property WindowProc: TWndMethod;

フォームにTWndMethod型のメソッドを作り,それをPanelのWindowProcプロパティに 代入する.ただし,WM_DROPFILES以外のメッセージは元のWPに 処理させるため,WindowProcプロパティに代入する前に,以前の値を保存しておかなければならない. 具体的なコードは以下のようになる.

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
  private
    procedure PanelWndProc( var Message: TMessage );
  public
    FOldWndProc: TWndMethod;
  end;

(省略)

// 置き換えるImageのウィンドウプロシージャ
procedure TForm1.PanelWndProc( var Message: TMessage );
var
  DroppedFile: array[ 0..255 ] of Char;
  Ext: String;
begin
   case Message.Msg of
    WM_DROPFILES:
    begin
       DragQueryFile( Message.WParam, 0, DroppedFile, SizeOf( DroppedFile ) );
       Ext := LowerCase( ExtractFileExt( DroppedFile ) );
       if Ext = '.bmp' then
          Image1.Picture.LoadFromFile( DroppedFile );
       DragFinish( Message.WParam );
    end;
   end;
   FOldWndProc( Message );
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  P: Integer;
begin
   DragAcceptFiles( Panel1.Handle, True );
   FOldWndProc := Panel1.WindowProc;   // 以前のウィンドウプロシージャを保存
   Panel1.WindowProc := PanelWndProc;  // 新しいウィンドウプロシージャに変更
end;

ImageコンポーネントのWPとして フォームのメソッドを使うというところがいささかトリッキーではあるが, これで一応所望の動作をするようになる.


お問い合わせはメールにて: akasaka@klc.ac.jp

戻る
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送