FAQ一覧へ インデックスへ

ドロップされたフォームにフォーカスを与えるには?


質問

2.0J で、アクティブでないフォームに、ファイルをドラッグ&ドロップした
とき、MessageDlg などの処理を入れると、そのフォームが MessageDlg ごと
後ろの方に隠れてしまいます。MessageDlg の前に、そのフォームにフォーカ
スをやればいいと思い、次のように、いろいろ試したのですが、ダメです。
どうすればいいのでしょう? Form1 に ListBox1 を配し、エクスプローラ
からドラッグして、ドロップしようとしています。

  (1) ListBox1.SetFocus;
  (2) Form1.SetFocus;
  (3) Application.NormalizeTopMosts;
  (4) FormStyle:=fsStayOnTop;         {注1}
  (5) SetActiveWindow(Form1.Handle);
  (6) BringWindowToTop(Form1.Handle);

 また、

  (7) SetWindowPos(Application.Handle, HWND_TOPMOST, 0,0,0,0,
        SWP_NOMOVE or SWP_NOSIZE);

を入れると、確かに MessageDlg は前面に出てくれるのですが、それにフォ
ーカスは来ません。

 因みに、1.0J では、(1)〜(7) を入れるまでもなく、MessageDlg は前面に
出てくれます。



答え

これは、

  SetForegroundWindow(Form1.Handle);

でできます(わかるまで地獄を見たりします!)。

【説明】
 SBORLAND MES 9 #15630 中野 和彦 さんの、

RE:?:ファイルのト゛ラック゛&ト゛ロッフ゜を受け付ける方法 (96/07/28)

のコードの一部を使って、説明させていただきます。

procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
...
begin
  if Msg.Message = WM_DROPFILES then begin
    {---この部分---}
    Files:= DragQueryFile(Msg.wParam, -1, nil, 0);
...
end;

で、{---この部分---} のところに、

    if MessageDlg('ディレクトリーが違います。'#13+
      '元のファイルと入れ替えますか?',
      mtWarning,mbOkCancel,0)=mrCancel then 
      exit;

というようなコードを入れると、Form1 が、この MessageDlg ごと、後ろの
方に行ってしまい、タスクバーなどで、このアプリを出さないと、この、
MessageDlg を処理できません。ドロップを受けた時点で、ここの、
AppMessage が動いているのに、上の (1)〜(6) がなぜダメなのか、その理由
らしきものを考えはしたのですが、考えているうちにわからなくなりました。
(^_^;)

 2.0 での、つまり、Win32 での SetActiveWindow は、これを呼び出した 
スレッドに所有されているウィンドウのみトップにもって来る、とあります
が、1.0 つまり、Windows3.1 API の方は、ウィンドウを任意にアクティブ化
して入力フォーカスを与える、となっていますので、2.0 で、WM_DROPFILES
メッセージを受け取っただけでは、スレッドは移らないのだろう、と、最初
思いましたが、実行してるってことはスレッドは移っているんだから、おか
しいな、と考え直し、まだ、答えを見つけられません。m(._.)m

 {注1} は、このコードじゃなくて、他の、FDELPHI LIB 5 Epsy さんの、

>1  GHG01155 96/09/25 4812 122 T DropAcc .PAS フォームにドロップを受入

のコンポーネントを使ってやったときですが、フォームの表示がおかしくなり
ましたので、ここでは試しませんでした。(3) は FormStyle を fsStayOnTop
にしてやっているんですが、ダメです。

 それから、(7) は、タイミングが、この場所 {---この部分---} では間に合
わないようで、MessageDlg 処理後に Form1 にフォーカスが来ます。ただし、
'TOPMOST' になっていますから、

  SetWindowPos(Application.Handle, HWND_NOTOPMOST, 0,0,0,0,
    SWP_NOMOVE or SWP_NOSIZE);

などで、戻してやった方が自然です。