我的应用程序可以让用户只在系统托盘中运行它,而不是在任务栏中运行它。当我的应用程序由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,因此答案似乎不适用。
[编辑:]更具体地说,我在这里添加其他信息。此应用程序有两种模式:在任务栏中显示,并在系统托盘中显示。
第一种模式与任何普通应用程序相同。该应用程序仅存在于任务栏中。它最小化到任务栏。它从任务栏恢复。
第二种模式的行为完全相同,但任务栏图标仅存在于系统托盘中。因此,当用户最小化应用程序时,我拦截该消息,抓取TRect为'Shell_TrayWnd'/'TrayNotifyWnd',并调用DrawAnimatedRects()来模拟托盘的最小化。然后我隐藏了主表格。在来自系统托盘的消息中,我反向绘制相同的动画,并使其再次可见。表单可见时,它不会显示在任务栏中。
这一切在所有Windows版本中都完美无缺。
我遇到的具体问题是,当显示任何其他表单时,Windows XP正在任务栏中创建应用程序图标。 Windows 7不会这样做。因此,如果Windows XP用户仅使用应用程序主表单,则不会出现任何问题,并且两种查看模式都可以正常工作。如果他们打开另一个窗口,则会出现应用程序图标,即使在该窗口关闭后也会停留在那里。 Windows 7不会这样做,图标也会消失。
答案 0 :(得分:8)
你应该设置
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;
或者另一种方法是通过OnMinimize
的{{1}}事件处理程序来禁止应用程序窗口显示。
TApplication
答案 1 :(得分:3)
大卫的回答是正确的。它有一些小问题,但我运行它并使一切正常。在我搞清楚时,他发布了他的最新更新。我在这里发布了一些额外的代码示例,并接受了他的回答。首先我指定了:
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”是否为真。如果它是FALSE,那么我知道用户点击X,我只是最小化调用此处引用的其他函数的应用程序。如果这是真的,我允许结束。
最后,MinimizeOnRestore抓取表格本身的TRect以及系统托盘,然后执行DrawAnimatedRects。这在Vista或更高版本上始终不起作用,但它也没有错误。接下来,它隐藏主应用程序窗口或使其可见。除非遇到错误,否则它总是返回true。然后它返回false。