我一直试图让我的应用程序中的一些无模式窗体显示在任务栏上 - 利用Windows 7中新的有用任务栏。
在任务栏上存在表单之前,VCL有许多问题需要撤消。
但最后一个问题是,最小化VCL指定主窗体的形式会导致应用程序中的所有窗口消失。
Ten years ago, Peter Below (TeamB) documented these problems,尝试解决这些问题。但是有些问题无法解决。这些问题在VCL本身内部如此深入,实际上不可能使Delphi应用程序正常运行。
这一切都源于您在工具栏上看到的按钮不代表应用程序窗口的事实;它代表TApplications
窗口,它是隐藏的,从未见过。然后是应用程序的MainForm
,然后充满了特殊的能力,如果它被最小化,那么它会指示应用程序隐藏自己。
在我看来,如果我能做到
Application.MainForm := nil;
然后所有这些错误都会消失。该应用程序可以有其隐藏窗口,同时我将覆盖应用程序中的每个 其他 表单,包括我的主表单, :
procedure TForm2.CreateParams(var params: TCreateParams );
begin
inherited CreateParams(params);
params.ExStyle := params.ExStyle or WS_EX_APPWINDOW;
end;
但在Delphi中,Application.MainForm
属性是只读的。
我怎么能在Delphi中没有MainForm
?
答案 0 :(得分:8)
如果未分配MainForm,则无法运行GUI项目。主消息循环将立即退出而没有一个。但是,这并不意味着MainForm必须运行您的UI。您可以使用空白隐藏的TForm作为指定的MainForm,然后让它将您的真实MainForm实例化为辅助TForm。例如:
HiddenMainFormApp.dpr:
project HiddenMainFormApp;
uses
..., Forms, HiddenMainForm;
begin
Application.Initialize;
Application.CreateForm(THiddenMainForm, MainForm);
Application.ShowMainForm := False;
Application.Run;
end.
HiddenMainForm.cpp:
uses
..., RealMainForm;
procedure THiddenMainForm.FormCreate(Sender: TObject);
begin
RealMainForm := TRealMainForm.Create(Self);
RealMainForm.Show;
end;
RealMainForm.cpp:
procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
Application.Terminate;
end;
可替换地:
HiddenMainFormApp.dpr:
project HiddenMainFormApp;
uses
..., Forms, HiddenMainForm, RealMainForm;
begin
Application.Initialize;
Application.CreateForm(THiddenMainForm, MainForm);
Application.ShowMainForm := False;
RealMainForm := TRealMainForm.Create(Application);
RealMainForm.Show;
RealMainForm.Update;
Application.Run;
end.
RealMainForm.cpp:
procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
Application.Terminate;
end;
答案 1 :(得分:5)
你不能,特别是在Delphi 5中。
关于TApplication窗口的引用是在任务栏上看到的,现在几个Delphi版本都没有(我相信D2007改变了它)。
因为您使用的是Delphi 5,所以您使用的是Delphi的过时副本;目前的版本几乎没有你要写的东西了。我建议你升级到更高版本的Delphi(D5非常老); Delphi 2007如果你需要避免使用Unicode,Delphi XE,如果你可以在VCL和RTL中使用(或者不介意)Unicode支持。
您所描述的内容不是错误,BTW。它们是在设计Delphi 1时做出的有意设计决策,并且通过Delphi 7可以正常使用Windows版本。更高版本的Windows(XP / Vista / Win7和等效的服务器版本)的变化使得该架构的变化成为必要,并且它们随着Delphi与Windows一起发展而制定。因为你已经选择不继续使用你的Delphi版本来保持它最近不会使你写的东西神奇地变成bug。 : - )
答案 2 :(得分:3)
分配Application.MainForm似乎不是问题,因为在最小化MainForm的同时在任务栏上显示另一个无模式窗体。
Project1.dpr:
program Project1;
uses
Forms,
Windows,
Unit1 in 'Unit1.pas' {MainForm},
Unit2 in 'Unit2.pas' {Form2};
{$R *.res}
var
MainForm: TMainForm;
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
ShowWindow(Application.Handle, SW_HIDE);
Application.Run;
end.
Unit1.pas:
unit Unit1;
interface
uses
Windows, Messages, Classes, Controls, Forms, StdCtrls, Unit2;
type
TMainForm = class(TForm)
ShowForm2Button: TButton;
ShowForm2ModalButton: TButton;
procedure ShowForm2ButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ShowForm2ModalButtonClick(Sender: TObject);
private
FForm2: TForm2;
procedure ApplicationActivate(Sender: TObject);
procedure Form2Close(Sender: TObject; var Action: TCloseAction);
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
implementation
{$R *.dfm}
procedure TMainForm.FormCreate(Sender: TObject);
begin
Visible := True; //Required only for MainForm, can be set designtime
Application.OnActivate := ApplicationActivate;
end;
procedure TMainForm.ApplicationActivate(Sender: TObject);
{ Necessary in case of any modal windows dialog or modal Form active }
var
TopWindow: HWND;
I: Integer;
begin
TopWindow := 0;
for I := 0 to Screen.FormCount - 1 do
begin
Screen.Forms[I].BringToFront;
if fsModal in Screen.Forms[I].FormState then
TopWindow := Screen.Forms[I].Handle;
end;
Application.RestoreTopMosts;
if TopWindow = 0 then
Application.BringToFront
else
SetForegroundWindow(TopWindow);
end;
procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
ExStyle := ExStyle or WS_EX_APPWINDOW;
WndParent := GetDesktopWindow;
end;
end;
procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
if Msg.CmdType = SC_MINIMIZE then
ShowWindow(Handle, SW_MINIMIZE)
else
inherited;
end;
{ Testing code from here }
procedure TMainForm.ShowForm2ButtonClick(Sender: TObject);
begin
if FForm2 = nil then
begin
FForm2 := TForm2.Create(Application); //Or: AOwner = nil, or Self
FForm2.OnClose := Form2Close;
end;
ShowWindow(FForm2.Handle, SW_RESTORE);
FForm2.BringToFront;
end;
procedure TMainForm.Form2Close(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
FForm2 := nil;
end;
procedure TMainForm.ShowForm2ModalButtonClick(Sender: TObject);
begin
with TForm2.Create(nil) do
try
ShowModal;
finally
Free;
end;
end;
end.
Unit2.pas:
unit Unit2;
interface
uses
Windows, Messages, Classes, Controls, Forms;
type
TForm2 = class(TForm)
private
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
implementation
{$R *.dfm}
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
ExStyle := ExStyle or WS_EX_APPWINDOW;
WndParent := GetDesktopWindow;
end;
end;
procedure TForm2.WMSysCommand(var Msg: TWMSysCommand);
begin
if Msg.CmdType = SC_MINIMIZE then
ShowWindow(Handle, SW_MINIMIZE)
else
inherited;
end;
end.
(在XP和Win7上用D5和D7测试过。)
(是的,你可能会认为这不是一个答案,因为它不是:还有一个主要形式。但我想这会回答问题背后的问题......)
答案 3 :(得分:2)
我不能代表Delphi 5,但是在Delphi 7中,如果你愿意弄脏你,你绝对可以在没有主变形的情况下运行。我在另一个答案here中介绍了很多细节。
由于Delphi 5没有MainFormOnTaskbar属性,您需要在dpr中执行以下操作:
// Hide application's taskbar entry
WasVisible := IsWindowVisible(Application.Handle);
if WasVisible then
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
if WasVisible then
ShowWindow(Application.Handle, SW_SHOW);
// Hide the hidden app window window from the Task Manager's
// "Applications" tab. Don't change Application.Title since
// it might get read elsewhere.
SetWindowText(Application.Handle, '');
这将隐藏应用程序窗口,只要您覆盖表单的 CreateParams 以设置Params.WndParent := 0
,每个人都将拥有自己的任务栏条目。 Application.MainForm未分配,因此最小化覆盖等问题不是问题,但您必须小心任何假定MainForm有效的代码。
答案 4 :(得分:0)
你可以将你的无模式表格放在一个dll中,然后它们就可以自己动作了。 (如果在创建它时不使用dll的Application实例(Application.CreateForm),则dll中的Application.Mainform为nil。)
当然,这可能不太可行,具体取决于表格可能需要做什么。
答案 5 :(得分:0)
实际上,你抱怨的大部分内容实际上都是Windows的设计,而不是VCL。有关所有详细信息,请参阅Windows Features。
问题的关键是所有者财产,我的意思是窗户所有者而不是VCL所有者。
隐藏拥有的窗口时 它的主人是最小化的。
如果您希望能够在不隐藏其他窗口的情况下最小化主窗体,那么您需要掌握所拥有的窗口的工作方式。