2014-09-04 112 views
5

我有这个问题。当我隐藏我的主窗体时,我的应用程序的任务栏图标也被隐藏起来。我也看到了关于这个问题的一个新问题,答案并没有真正的帮助。他们建议尽量减少它,但我不想最小化应用程序。Delphi在应用程序运行时更改主窗体

当应用程序已经运行时,是否可以更改主窗体?例如

。我有两种形式。当我想隐藏一个窗体并显示其他窗体时,任务栏图标应该保留在任务栏上,并且主窗体应该切换到另一个窗体。

我正在使用Delphi XE6,它是一个VCL Forms应用程序。

我也看到了有关更改而运行的主要形式不同的老问题,但它已经很老了,仍然基于Delphi 6

+0

你可以改变'指针((@ Application.MainForm)^)的主要形式有:=窗体2;'。 – linluk 2014-09-04 15:15:41

+1

@linluk:这是不好的建议。 'MainForm'是一个属性,而不是一个变量。你不能得到一个属性的地址,所以你实际上得到了该属性返回的指针的地址。然后你修改THAT指针指向一个不同的对象,你不会修改TApplication本身内部的指针。 – 2014-09-04 16:28:08

+0

确实听起来很危险:S,它工作到目前为止,我知道这是一个肮脏的黑客攻击,但我会寻找一个更好的解决方案,为我将来的应用程序,我使用它(也许通过rtti设置变量,或者其他)或尝试避免改变主窗体:) – linluk 2014-09-05 06:25:58

回答

2

正如David Heffernan已经表示,不可能更改已经运行的应用程序的主窗体。这是窗户本身的限制。

你可以做的就是作弊,永远不会改变主窗体,但只会使它看起来像你一样。
你是如何实现这一目标的?

第1步:将代码添加到第二种形式作出自己的任务栏按钮

procedure TWorkForm.CreateParams(var Params: TCreateParams); 
begin 
    inherited; 
    Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; 
end; 

第2步:刚切换到它之前动态创建第二种形式。在创建之前添加的代码将为您的第二个表单创建一个新的任务栏按钮。

第3步:现在隐藏您的实际主窗体。隐藏它也会隐藏属于它的任务栏按钮。所以你最终还是会显示一个任务栏按钮,它是属于你的第二个窗体的那个。

步骤4:为了让您的第二个窗体在其闭包调用中终止您的应用程序从您的第二个窗体OnClose或OnFormCloseQuery事件中关闭您的真正主窗体的方法。
如果您想要切换回主窗体的主窗体调用Show方法而不是Close方法。

这种方法允许我们快速交换表单,所以只有最敏锐的用户才会注意到任务栏按钮的短动画。
注意:如果你的第二个是复杂的,并且因为这需要一些时间来创建,你可能会创建它隐藏,然后一旦它的创建过程完成显示它并进行交换。否则,您最终可能会同时显示两个任务栏按钮,我相信您想避免这些按钮。

下面是一个简单的例子:
- LoginForm的是一个真正的主要形式,是在应用程序启动 创造 - 工作表单的是哪个用户会花大部分的时间在登录后的表格,这一次是在登录创建过程

登录表单代码:

unit ULoginForm; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; 

type 
    TLoginForm = class(TForm) 
    BLogIn: TButton; 
    procedure BLogInClick(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    LoginForm: TLoginForm; 

    //Global variable to tell us if we are only logging out or closing our program 
    LoggingOut: Boolean; 

implementation 

uses Unit2; 

{$R *.dfm} 

procedure TLoginForm.BLogInClick(Sender: TObject); 
begin 
    //Create second Form 
    Application.CreateForm(TWorkForm, WorkForm); 
    //Hide Main Form 
    Self.Hide; 
    //Don't forget to clear login fields 
end; 

end. 

工作表单代码:

unit UWorkForm; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; 

type 
    TWorkForm = class(TForm) 
    BLogOut: TButton; 
    //Used in overriding forms creating parameters so we can add its own Taskbar button 
    procedure CreateParams(var Params: TCreateParams); override; 
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
    procedure BLogOutClick(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    WorkForm: TWorkForm; 

implementation 

uses Unit1; 

{$R *.dfm} 

procedure TWorkForm.BLogOutClick(Sender: TObject); 
begin 
    //Set to true so we know we are in the process of simply logging out 
    LoggingOut := True; 
    //Call close method to begin closing the current Form 
    Close; 
end; 

procedure TWorkForm.CreateParams(var Params: TCreateParams); 
begin 
    inherited; 
    Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; 
end; 

procedure TWorkForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
begin 
    //Check to see if we are in the process of simply logging out 
    if not LoggingOut then 
    begin 
    //If we are not in the process of logging out close the Main Form 
    LoginForm.Close; 
    //and then allow closing of current form 
    CanClose := True; 
    end 
    else 
    begin 
    //But if we are in the process of simply logging out show the Main Form 
    LoginForm.Show; 
    //Reset the LoggingOut to false 
    LoggingOut := False; 
    //and then alow closing of current form 
    CanClose := True; 
    end; 
end; 

end. 
+2

永远不要将窗户所有者设置为桌面。 http://blogs.msdn.com/b/oldnewthing/archive/2004/02/24/79212.aspx – 2014-09-04 16:09:40

+2

- *“这是Windows本身的限制。”* - Windows不关心,如果你有一个主要形式与否。它甚至没有主要形式的概念。 – 2014-09-09 18:24:31

4

是否有可能改变的主要形式,而应用程序已经运行?

在程序运行时,无法更改VCL主窗体。该属性在程序启动时一劳永逸地确定。

一种可能的解决方法是安排辅助窗体,即不是主窗体的窗体,在任务栏上有一个按钮。通过使其变为无主或通过使用扩展窗口样式来实现这一点。

更新

好了,你可以改变Application.MainForm,但它需要你销毁当前主要形式,然后创建一个新的。

+0

好的谢谢。我曾考虑过这个问题。为什么当我隐藏我的主窗体时,它会隐藏任务栏图标?因为我的应用程序仍在运行,只有窗体被隐藏。 – 2014-09-04 13:29:05

+0

按设计,任务栏上不显示隐藏的窗口:http://msdn.microsoft.com/en-gb/library/windows/desktop/cc144179.aspx最小化窗口是为任务栏按钮设置简单方法一个无法看到的窗口 – 2014-09-04 13:32:25

+0

@DavidHeffernan我认为可以更改主窗体。我知道它,因为我在我的一个应用程序中使用它。我这样做:'指针((@ Application.MainForm)^):= Form2;'它工作正常。我可以在不关闭应用程序的情况下关闭旧的主窗体,并且如果我关闭了新的主窗体,应用程序按预期终止。 – linluk 2014-09-04 15:12:30

2

如果在启动例程中(在文件中)将Application.MainFormOnTaskbar设置为false,则VCL将创建一个隐藏表单,其唯一目的是提供任务栏图标。这是一种较旧的方法,通常不推荐使用,但只要其他窗口可见,则可让您隐藏主窗体,而不会使应用程序从任务栏中消失。

您也可以提供一个Application.OnGetMainFormHandle事件处理程序来改变你的Application.MainFormHandle在运行时(而不是Application.MainForm)。 MainFormHandle影响像模态弹出对话框的所有者之类的东西。

有关Application.MainFormOnTaskbar的更多信息以及禁用它的缺点:此操作很匆忙。简短版本位于VCL docs,这说明在Vista中引入的几个Windows新功能(如实时任务栏缩略图)需要MainFormOnTaskbar := True。在this SO discussion中有更多的背景阅读。

+0

这种方法的负面影响是什么? (更改Application.MainFormOnTaskbar为false) – 2014-09-04 13:35:52

+0

@DavidHeffernan - 它在我在XE4中测试时起作用。在创建任何表单之前,必须在'.dpr'中完成分配。 – 2014-09-04 13:40:39

+0

哈哈不,我不知道该怎么做。你怎么做呢? – 2014-09-04 13:40:39

3

一旦分配了Application.MainForm就无法更改。但是,你也不需要。这个问题的最简单的解决方案是创建一个空白的隐藏的TForm作为实际的Application.MainForm并让它正常管理任务栏,然后您可以在需要时显示/隐藏任何第二个TForm对象,您希望的“MainForm”是次要形式不是真正的MainForm

0

您可以更改主窗体。 做一个变量F:^ TForm,然后将其设置为@ Application.MainForm。之后,您可以通过F ^:= YourAnotherForm设置主窗体。

+0

美观大方的解决方案! – Zam 2015-07-28 14:59:41

0

我有一个额外的问题与Delphi XE2 MDI应用程序,也是COM服务器。我的主窗体Main调用一个辅助窗体MenuForm,其中包含整个应用程序的共享图标和弹出菜单。

在我的示例中,该应用程序在独立运行时可以很好地工作。

Main被创建,然后在FormCreate的最后创建一个MenuForm。 一切都好。

但是当从COM服务器调用时,首先调用Main.FormCreate,但不知何故MenuForm被分配给Application.MainForm。基础RTL中存在竞争条件。这在尝试创建第一个SDI子时会造成严重破坏,因为Application.MainForm不是MDI。

我试着通过Main.FormCreate发送一条消息回到自身,以便在Main.FormCreate返回之前延迟创建MenuForm。

但是这里仍然存在竞争状态 - MenuForm仍然被分配给Application.MainForm。

我最终能够解决这个问题,使用代码轮询Application.MainForm每10ms,最多10秒。我还必须在MainForm的图标列表中(在.dfm中)删除任何引用,直到明确创建MenuForm后 - 否则MenuForm将隐式创建在MainForm.create的末尾。

希望这可以帮助别人!

const 
    CM_INITIAL_EVENT = WM_APP + 400; 


TmainForm = class(TForm) 
    ... 
    procedure afterCreate(var Message: TMessage); message CM_INITIAL_EVENT; 
    ... 
end; 


procedure TmainForm.FormCreate(Sender : TObject); 
begin 
    ... 
    ...standard init code 
    ... 

    postmessage(handle, CM_INITIAL_EVENT, 0, 0); 
End; 


procedure TmainForm.AfterCreate(var Message: TMessage); 
var 
    i: Integer; 
begin 

    //must assign these AFTER menuform has been created 
    if menuForm = nil then 
    begin 
    //wait for mainform to get assigned 
    //wait up to 10*1000ms = 10 seconds 
    for i := 0 to 1000 do 
    begin 
     if Application.Mainform = self then break; 
     sleep(10); 
    end; 

    Application.CreateForm(TmenuForm, menuForm); 
    menuForm.parent := self; 
    end; 

    //NOW we can assign the icons 
    Linktothisfilterfile1.SubMenuImages := menuForm.treeIconList; 
    ActionManager.Images := menuForm.treeIconList; 
    allFilters.Images := menuForm.treeIconList; 
    MainMenu.Images := menuForm.treeIconList; 
    ... 
end; 
0

在当前的Delphi实现中,我确信使用指向Application.MainForm的指针没有任何后果。

如果查看TApplication类,可以看到FMainForm仅用于检查应用程序是否至少有一个表单,并且在FMainForm存在时迭代TApplication.Run方法内的主循环。如果你不想使用指针来破解这个属性,你可以实现自己的TApplication类,比如TMyApplication,复制其中的所有例程,并定义MainForm属性来读写

2

我以相同的方式实现了它@DavidHeffernan已经提出并且还没有遇到任何问题,但它可能不是最好的方式,但它对我和我想要实现的东西是有效的,当用户最小化他们的主要形式时这具有“正常”的感觉行为。

代码看起来与此类似:

procedure TfrmLogin.btnLoginClick(Sender: TObject); 
begin 
    frmMainWork := TfrmMain.Create(Application); 
    try 
     Pointer((@Application.MainForm)^) := frmMainWork; 
     frmLogin.Hide; 
     frmMainWork.ShowModal; 
    finally 
     Pointer((@Application.MainForm)^) := frmLogin; 
     frmLogin.Show; 
     FreeAndNil(frmMainWork); 
    end; 

end; 

希望这有助于:-)

相关问题