Delphi Tips 
-----------------------------

0228  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/08/29 西坂良幸 rev 1.4
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/09/01 西坂良幸 編集
フォームの最小化、最大化をアニメーションでやりたい。(DELPHI)


過去のMLを検索してみましたが案外少ないようです。「最小化」の話題は結構あるのに。このような機能をもつコンポーネントがたくさんあるし、あまり重要な機能でもないということですか。

これは当然ですが、SetBoundsメソッドなんかをいじっていてもダメですね。
また、WM_SYSCOMMANDメッセージをとらえて
procedure TForm1.WMSysCommand(var msg: TWMSysCommand);
begin
  if msg.CMdType = SC_MINIMIZE then
    WindowState := wsMinimized;
  inherited;
end;

としてもだめですね。[Delphi-ML:939]

結論的に言うと、MovWindowというAPIを使うんですが、このためのちょっとした工夫が、メッセージの理解やその処理の仕方について大変勉強になります。(コンポーネント貼り付けるだけが能ではない!)

ということで、今回はちょっと解説を。
・フォーム(メインも含めて)とApplicationの同期を考慮しなければならないので、双方のWindProcをオーバーライドしなければならない。
・自分のメソッドのWindProcは、オーバーライドは簡単ですよね、
・では、ApplicationのWndProcはどうすればオーバーライドのようにできるのか。
というようなことを考えて下さい。

  TForm1 = class(TForm)
    PopupMenu1: TPopupMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;        // ボタンではなく、メニューでテストがミソ
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure N1Click(Sender: TObject);
    procedure N2Click(Sender: TObject);
  private
    NewAppWndProc: Pointer;
    OldAppWndProc: Pointer;
  protected
    procedure WndProc(var Msg: TMessage); override;
    procedure AppWndProc(var Msg: TMessage); virtual;
  public
  end;

//  途中省略

uses MMSystem;

{$R *.DFM}
// AppliCationのWndProcをすり替えている
procedure TForm1.FormCreate(Sender: TObject);
begin
  if Application.MainForm = nil then
  begin
    // WndProcのインスタンスを作成
    NewAppWndProc := MakeObjectInstance(AppWndProc);
    // 元のApplicationのWndProcアドレスを保存
    OldAppWndProc := Pointer(GetWindowLong(Application.Handle, GWL_WNDPROC));
    // ApplicationのWndProcアドレスをNewAppWndProcに変更
    SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(NewAppWndProc));
  end;
end;

// 終わる時は後始末をする
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if (Application.Handle <> 0) and (OldAppWndProc <> nil) then
  begin
    // 元のWndProcに戻す
    SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(OldAppWndProc));
    // インスタンスを解放
    FreeObjectInstance(NewAppWndProc);
  end;
end;

//ウィンドウプロシージャを書き直す
procedure TForm1.WndProc(var Msg: TMessage);
var
  SaveTitle: string;
begin
  with Msg do  // ここでメッセージを横取り
  begin
    if (Msg = WM_SYSCOMMAND) and (WParam = SC_MINIMIZE) then
      with Application do
      begin
        SaveTitle := Title;
        Title := Caption;
        // 再び手前に表示−−おまじない
        NormalizeTopMosts;
        // トップ レベルでアクティブ ウィンドウ化
        SetActiveWindow(Handle);
        // ウィンドウの位置と寸法を変更
        MoveWindow(Handle, Left, Top, Width, Height, True);
        // サウンドを再生  ふろくだから無視してください
        PlaySound('Minimize', 0, Snd_Alias or Snd_NoDefault or Snd_ASync);
        // 表示状態を設定
        ShowWindow(Handle, SW_MINIMIZE);
        Title := SaveTitle;
      end
  end;
  inherited WndProc(Msg);
end;

//アップリケーションプロシージャに置き換えられた偽のWndProc
procedure TForm1.AppWndProc(var Msg: TMessage);
begin
  with Msg do     // ここでも横取りする
  begin
    if (Msg = WM_ERASEBKGND) then
      Result := 1
    else if (Msg = WM_SYSCOMMAND) and (WParam = SC_RESTORE) then
      with Application do
      begin   // 戻す
        PlaySound('RestoreUp', 0, Snd_Alias or Snd_NoDefault or Snd_ASync);
        // トップ レベルでアクティブ ウィンドウ化
        SetActiveWindow(Handle);
        ShowWindow(Handle, SW_RESTORE);
        // わり算を大きくするとスム−ス
        MoveWindow(Handle, Screen.Width div 4, Screen.Width div 4, 0, 0, False);
        Result := 1;
      end
    else if (Msg = WM_SYSCOMMAND) and (WParam = SC_MINIMIZE) then
    begin    // 最小化
      PlaySound('Minimize', 0, Snd_Alias or Snd_NoDefault or Snd_ASync);
      // フォーム
      Perform(WM_SYSCOMMAND, SC_MINIMIZE, 0);
    end
    else
      Result := CallWindowProc(OldAppWndProc, Application.Handle, Msg, WParam, LParam);
  end;
end;


これをヒントに自分でコンポーネント化するのは簡単でしょう。
もう一つ、フォームへのメッセージを横取りするWndProcをコンポーネントで書くということです。

MLで話題になっていたので、ひとこと。ボタンやメニューで最小化する時は

procedure TForm1.N1Click(Sender: TObject);
begin
  Application.minimize;  // これはダメ アニメーションしない(当然)
end;

procedure TForm1.N2Click(Sender: TObject);
begin
  Perform(WM_SYSCOMMAND, SC_MINIMIZE, 0); // これでうまくいく
end;

逆のRestoreの場合は、
  Application.minimize;
  Perform(WM_SYSCOMMAND, SC_RESTORE, 0);
でも似たようなものになります。
なぜか?  → MLを再読されたし。
参照: [Delphi-ML:939] [Delphi-ML:34432] <Windows> <フォーム>

[新規作成] [最新の情報に更新]

How To
Lounge
KeyWords


Tips
Delphi
Home
Osamu Takeuchi osamu@big.or.jp