我的应用程序有一个选项供用户仅在系统托盘中运行,而不是在任务栏中运行。当我的应用程序由 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 中一切正常。
知道我遗漏了什么吗?
我应该补充一点,我知道这可能是与 Hide the Main Form in a Delphi 2009 Application 相同的问题,但是我已经设置了 MainFormOnTaskBar,因此该答案似乎不适用。
[编辑:] 更具体地说,我在这里添加了额外的信息。此应用程序有两种模式:在任务栏中显示,在系统托盘中显示。
第一种模式与任何普通应用程序相同。该应用程序仅存在于任务栏中。它最小化到任务栏。它从任务栏恢复。
第二种模式的行为完全相同,但任务栏图标只存在于系统托盘中。因此,当用户最小化应用程序时,我拦截该消息,获取“Shell_TrayWnd”/“TrayNotifyWnd”的 TRect,并调用 DrawAnimatedRects() 来模拟最小化到托盘。然后我隐藏主窗体。在来自系统托盘的消息中,我反向绘制相同的动画矩形,并使其再次可见。虽然表单可见,但它不会显示在任务栏中。
这一切在所有 Windows 版本中都运行良好。
我遇到的具体问题是,当显示任何其他窗体时,Windows XP 正在任务栏中创建应用程序图标。 Windows 7 不这样做。因此,如果 Windows XP 用户只使用应用程序主窗体,则不会出现任何问题,并且两种查看模式都可以正常工作。如果他们打开另一个窗口,应用程序图标就会出现,并且即使在该窗口关闭后也会保留在那里。 Windows 7 不执行此操作,图标保持消失。
最佳答案
你应该设置
Application.MainFormOnTaskBar := True;
在您的 .dpr 文件中,然后永远不要修改该设置。
然后,当您想从任务栏中删除主窗体时,只需编写
MainForm.Hide;
当你需要再次将主窗体隐藏起来时,写
MainForm.Show;
就是这样。
自然地,您会希望在隐藏和显示主窗体的同时显示和隐藏您的通知区域图标。
HideWindowFromTaskbar
中的代码不是必需的,您应该将其删除。当您的应用程序处于 MainFormOnTaskBar
等于 True
模式时,主窗体是一个无主窗口。因此,只要它可见,它就会出现在任务栏上。所以您可以从任务栏中删除主窗体,只需将其隐藏即可。
您应用程序中的其他窗体将拥有顶级窗口。通常它们将归您的主窗体所有。由于是所有者,他们不会出现在任务栏上。
总的来说,您应该尽量避免摆弄窗口样式。您通常可以让您的应用程序按照您需要的方式运行,而无需这样做。此外,如果您必须调整窗口样式,则必须在 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
设置为 False
。这意味着 Application.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.
运行此程序并单击切换按钮。现在您将看到主窗体和其他窗体显示。任务栏里什么也没有。我包含了切换按钮,以表明您可以在程序运行时在两种操作模式之间切换。无需重新启动它。
这里的关键是使可见窗体以外的窗口成为与任务栏关联的窗口。完成后,您可以通过显示和隐藏该窗口再次控制任务栏的存在。在这种情况下,该窗口是应用程序窗口 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;
或者另一种方法是通过 TApplication
的 OnMinimize
事件处理程序来抑制应用程序窗口显示。
class procedure TEventHandlerClass.ApplicationMinimize(Sender: TObject);
begin
ShowWindow(Application.Handle, SW_HIDE);
end;
关于windows - 如何停止在任务栏上显示我的应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14587456/