5

私のアプリケーションには、ユーザーがタスク バーではなくシステム トレイでのみ実行するオプションがあります。アプリケーションが Delphi 6 でビルドされたとき、これは正常に機能しました。Delphi XE2 に切り替えた後、機能しなくなりました。

私はそれをいじりましたが、これは Windows 7 で動作していますが、Windows XP で実行しているときはまだ問題があります。アプリケーションがタスク バーから正しく非表示になり、システム トレイに表示されます。しかし、追加のフォームを作成して表示すると、Windows XP でアイコンが表示されます。

procedure TfrmAppointment.HideWindowFromTaskbar;
var
   TaskbarList: ITaskbarList;
begin
Application.MainFormOnTaskBar := False;

// Windows 7 seems to behave differently.  This seems to fix it.
if (CheckWin32Version(6, 1)) then
    begin
    // We are in Win7, and we requested the tray.
    TaskbarList := CreateComObject(CLSID_TaskbarList) as ITaskbarList;
    TaskbarList.HrInit;
    TaskbarList.DeleteTab(Application.Handle);
    end
else
   begin
   // Previous code from D6 days
   ShowWindow(Application.Handle, SW_HIDE);
   SetWindowLong(Application.Handle, GWL_EXSTYLE, GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
   ShowWindow(Application.Handle, SW_SHOWNOACTIVATE);
   end;
end;

システム トレイにアプリケーションを表示するオプションをユーザーが選択すると、そのコードが実行されます。私がテストしたすべてのバージョンの Windows で問題なく動作します。ただし、Windows XP では、子フォームを表示すると、アプリケーションがタスクバーに即座に表示されます。Windows 7 ではすべて問題ありません。

私が見逃しているアイデアはありますか?

これはDelphi 2009 アプリケーションのメイン フォームを非表示にする と同じ質問である可能性が高いことを知っていることを付け加えておく必要がありますが、既に MainFormOnTaskBar が設定されているため、その答えは当てはまらないようです。

[編集:] より具体的に言うと、ここに追加情報を追加します。このアプリケーションには、タスク バーに表示するモードとシステム トレイに表示するモードの 2 つのモードがあります。

最初のモードは、通常のアプリケーションと同じです。アプリケーションはタスク バーにのみ存在します。タスクバーに最小化します。タスクバーから復元します。

2 番目のモードはまったく同じように動作しますが、そのタスク バー アイコンは代わりにシステム トレイにのみ存在します。したがって、ユーザーがアプリケーションを最小化すると、そのメッセージをインターセプトし、'Shell_TrayWnd'/'TrayNotifyWnd' の TRect を取得し、DrawAnimatedRects() を呼び出して、トレイへの最小化をシミュレートします。次に、メイン フォームを非表示にします。システム トレイからのメッセージで、同じアニメーション四角形を逆に描画し、再び表示します。フォームが表示されている間、タスク バーには表示されません。

これはすべて、すべての Windows バージョンで問題なく動作します。

私が抱えている特定の問題は、他のフォームが表示されると、Windows XP がタスク バーにアプリケーション アイコンを作成していることです。Windows 7 はこれを行いません。したがって、Windows XP ユーザーがアプリケーションのメイン フォームのみを使用する場合、問題は発生せず、両方の表示モードが正常に機能します。別のウィンドウを開くと、アプリケーション アイコンが表示され、そのウィンドウを閉じた後もそこにとどまります。Windows 7 はこれを行わず、アイコンは表示されません。

4

2 に答える 2

9

設定する必要があります

Application.MainFormOnTaskBar := True;

.dpr ファイルに追加し、その設定を変更しないでください。

次に、タスクバーからメインフォームを削除したい場合は、単に次のように記述します

MainForm.Hide;

メインフォームを再び非表示にする必要がある場合は、次のように記述します

MainForm.Show;

以上です。

当然、メイン フォームの表示と非表示に合わせて、通知領域のアイコンの表示と非表示を切り替える必要があります。


のコードHideWindowFromTaskbarは不要であり、削除する必要があります。アプリケーションがMainFormOnTaskBarequalsTrueモードの場合、メイン フォームは所有されていないトップレベル ウィンドウです。そのため、表示されているときはいつでもタスクバーに表示されます。したがって、メイン フォームを非表示にするだけで、タスク バーからメイン フォームを削除できます。

アプリケーションのその他のフォームは、最上位ウィンドウに所有されます。通常、それらはメイン フォームによって所有されます。所有者であるため、タスクバーには表示されません。

概して、ウィンドウ スタイルをいじるのを避けるように努力する必要があります。通常は、そうしなくても、必要な方法でアプリケーションを動作させることができます。さらに、ウィンドウ スタイルを調整する必要がある場合は、 で行う必要がありますCreateParams。そうすれば、ウィンドウが再作成されたときにウィンドウ スタイルが保持されます。ただし、繰り返しますが、できる限りウィンドウ スタイルを変更しないでください。

主な MSDN リファレンスは次のとおりです。


この点を証明する、私が作成できる最小のプログラムを次に示します。

program MainFormHiding;

uses
  Forms, StdCtrls;

var
  MainForm, OtherForm: TForm;
  Button: TButton;

type
  TEventHandlerClass = class
    class procedure ToggleMainFormVisible(Sender: TObject);
  end;

class procedure TEventHandlerClass.ToggleMainFormVisible(Sender: TObject);
begin
  MainForm.Visible := not MainForm.Visible;
end;

begin
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm, MainForm);
  OtherForm := TForm.Create(Application);
  MainForm.Caption := 'Main Form';

  OtherForm.Visible := True;
  OtherForm.Caption := 'Other Form';
  Button := TButton.Create(OtherForm);
  Button.Caption := 'Toggle';
  Button.Parent := OtherForm;
  Button.OnClick := TEventHandlerClass.ToggleMainFormVisible;

  Application.Run;
end.

コメントでは、メイン フォームを非表示にせずにタスク バー ウィンドウを非表示にしたいことを明確にしています。その場合は、 に設定MainFormOnTaskbarすることをお勧めしますFalseApplication.Handleこれは、タスクバー ボタンに関連付けられたウィンドウになることを意味します。次に、そのウィンドウを非表示にして、タスクバーから削除できます。

PopupParent補助フォームを明示的に設定する必要があります。これらのウィンドウをメイン フォームに所有させたい場合は、それを設定できます。

このシナリオに合わせて調整した例を次に示します。

program MainFormHiding;

uses
  Forms, StdCtrls, Windows;

var
  MainForm, OtherForm: TForm;
  Button: TButton;

type
  TEventHandlerClass = class
    class procedure ToggleTaskbarButton(Sender: TObject);
  end;

class procedure TEventHandlerClass.ToggleTaskbarButton(Sender: TObject);
begin
  if IsWindowVisible(Application.Handle) then
    ShowWindow(Application.Handle, SW_HIDE)
  else
    ShowWindow(Application.Handle, SW_SHOW);
end;

begin
  Application.MainFormOnTaskbar := False;
  Application.CreateForm(TForm, MainForm);
  OtherForm := TForm.Create(Application);
  OtherForm.PopupParent := MainForm;
  MainForm.Caption := 'Main Form';
  Application.Title := MainForm.Caption;

  OtherForm.Visible := True;
  OtherForm.Caption := 'Other Form';
  Button := TButton.Create(OtherForm);
  Button.Caption := 'Toggle';
  Button.Parent := OtherForm;
  Button.OnClick := TEventHandlerClass.ToggleTaskbarButton;

  Application.Run;
end.

このプログラムを実行し、トグル ボタンをクリックします。これで、メイン フォームとその他のフォームが表示されます。そして、タスクバーには何もありません。プログラムの実行中に 2 つの操作モードを切り替えることができることを示すために、トグル ボタンを含めました。再起動する必要はありません。

ここで重要なのは、表示されているフォーム以外のウィンドウをタスクバーに関連付けられたウィンドウにすることです。これを行うと、そのウィンドウを表示および非表示にすることで、タスクバーの存在を再び制御できます。この場合、そのウィンドウはアプリケーション ウィンドウですApplication.Handle。これはタスク バー上のウィンドウであるため、Titleプロパティを設定してテキストを制御する必要があります。

最後にもう一度強調しておきますが、タスク バーとの対話は、ウィンドウの所有者と可視性によって制御するのが最適です。ITaskbarList、拡張ウィンドウスタイルなどではなく、常にこれらの方法を使用してソリューションを検索してください。


アップデート

うまくいけば、この件に関する最後の言葉です。お気づきのように、メイン フォームが最小化されている場合、すぐ上のコードの動作は適切ではありません。これが発生すると、アプリケーション ウィンドウが再び表示されるようになり、タスク バーにもう一度表示されます。

この行動を抑えることに関しては、私には自信がありません。TApplication.Minimizeこの動作は、メイン フォームが最小化されたときにアプリケーション ハンドルを示すコードが原因で発生します。私が持っている最善の解決策は、メインフォームの最小化を非表示に変換することです。

procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;

....

procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
  if (Msg.CmdType and $FFF0)=SC_MINIMIZE then 
  begin
    Hide;
    exit;
  end;
  inherited;
end;

または、 のOnMinimizeイベント ハンドラを使用して、アプリケーション ウィンドウの表示を抑制する方法もありますTApplication

class procedure TEventHandlerClass.ApplicationMinimize(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
end;
于 2013-01-29T16:36:05.297 に答える
4

デビッドの答えは正しいです。いくつかの小さな問題がありましたが、実行してすべてが機能しました。私がこれを理解している間に、彼は最後の更新を投稿しました。ここにいくつかの追加のコード サンプルを投稿し、彼の回答を受け入れました。最初に割り当てました:

Application.OnMessage := AppMessage;

次に、手順は次のとおりです。

procedure TfrmAppointment.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
// This first check decides if we are minimizing via the upper right button OR
// The context menu in the upper left hand corner of the window. 
// Minimizing twice restores, so this can be a restore as well.
if ((((Msg.message = WM_NCLBUTTONDOWN) and (Msg.wParam = HTMINBUTTON)) or
     ((Msg.message = WM_SYSCOMMAND) and (Msg.wParam = SC_MINIMIZE))) and
    (Screen.ActiveForm = Self)) then
   begin
   // This function is defined as (bool, bool) where the variables are:
   // Param1: Mimimizing (true), Restoring (false)
   // Param2: Draw animation rectangles for doing this or not
   Handled := MinimizeOrRestore(Self.WindowState <> wsMinimized, True);
   end
else if ((Msg.message = WM_SYSCOMMAND) and 
         (Msg.wParam = SC_RESTORE) and 
         (Screen.ActiveForm = Self)) then
   begin
   // Specifically, restore has been asked for
   Handled := MinimizeOrRestore(False, True); // Minimize with animation
   end
else if ((Msg.message = WM_SYSCOMMAND) and (Msg.wParam = SC_CLOSE)) then
   begin
   // The user just used the system menu to close the application
   ApplicationIsClosing := True; // see below for this
   end
end;

次に、FormCloseQuery で、「ApplicationIsClosing」が true であることを確認します。FALSE の場合、ユーザーが X を押したことがわかり、ここで参照されている他の関数を呼び出すアプリケーションを最小化します。それが本当なら、私はクローズを許可します。

最後に、MinimizeOnRestore はフォーム自体とシステム トレイの TRect を取得し、DrawAnimatedRects を実行します。これは、Vista 以降では常に機能するとは限りませんが、エラーにもなりません。次に、メイン アプリケーション ウィンドウを非表示にするか、表示します。エラーが発生しない限り、常に true を返します。その後、false を返します。

于 2013-01-29T20:43:44.990 に答える