Delphi,如何使独立的窗口 [英] Delphi, how to make independent windows
问题描述
Kind Regards
Roy M Klever
这不是很难做到这一点,虽然它开始变得复杂很快,取决于你想要的完整程度。让多个模态对话单独工作是一大堆努力。
要开始,您需要完全避免Application.MainForm。始终使用 Form:= TMyForm.Create(Application)
而不是 Application.CreateForm(TMyForm,Form)
。后来设置MainForm,你永远不会想要这样做。
为了使事情正常关闭,您需要在表单的 OnClose
事件处理程序:
如果Screen.FormCount = 1然后
Application.Terminate ;
CloseAction:= caFree;
Application.Run
依赖于MainForm被分配,所以在你的DPR中用这个循环替换这一行:
repeat
try
Application.HandleMessage;
除了
Application.HandleException(Application);
结束
,直到Application.Terminated;
有几种方法来处理任务栏条目。
-
单个任务栏条目:设置
Application.MainFormOnTaskbar:= False;
,并使用隐藏的TApplication句柄。单击任务栏条目将所有窗口都放在前面。您需要覆盖Application.OnMessage
或添加TApplicationEvents
组件,并观看WM_CLOSE
与Msg.Handle = Application.Handle`。在这种情况下,用户已右键单击任务栏并选择了关闭,因此您应该关闭所有窗口。 -
多个任务栏条目:设置
Application.MainFormOntaskbar:= True
。覆盖您的表单的CreateParams
方法并设置Params.WndParent:= 0;
。每个任务栏条目将控制该表单。
可能还有一些其他问题,但这是基础知识。 p>
正如我所说,使 ShowModal
和 TOpenDialog / TSaveDialog
独立工作,所以它只影响其父表单,因此可以一次打开多个对话框,这是一大堆工作,我无法真正推荐它。如果你是一个受虐狂者,这里是一般步骤:
-
替换
TCustomForm.ShowModal
具有自定义版本。除此之外,该例程禁用应用程序中的所有其他窗口,因此您需要使用EnableWindow(Owner.Handle)替换
以禁用父表单。此时,您可以打开多个对话框,但只能以最新的先出顺序关闭,因为调用最终会被递归。如果没有,请停在这里。DisableTaskWindows / EnableTaskWindows
调用,False / True)
有两种方法可以解决:
-
不要让
ShowModal
阻止,而是StartModal
和EndModal
例程,它们具有ShowModal代码的第一位和最后一位,并在对话框关闭时调用
OnShowModalDone
事件。这是一种痛苦的使用,但相对容易的代码和易于稳定。 -
使用Windows 光纤例程,以交换堆栈并开始新的消息循环。这种方法很容易使用,因为
ShowModal
正在阻止,所以你像普通的这样调用它。这是我们在Beyond Compare中使用的方法。 不要这样做。写入很复杂,如果有全局消息挂钩,或者您的代码没有光纤安全,可能会遇到麻烦。
-
-
常见的对话框(TOpenDialog,TColorDialog等)也有类似的限制。要使它们只禁用父表单,您需要覆盖
TCommonDialog.TaskModalDialog
,并将DisableTaskWindows / EnableTaskWindows
调用替换为。它们不能像上面的常规Delphi对话框那样异步,因为它们阻止Windows提供的功能( GetOpenFileName , ChooseColor 等)。允许以任何顺序关闭的唯一方法是让每个对话框在专用线程中运行。只要您注意VCL对象的访问权限,Windows就可以处理大部分的同步,但这主要涉及重写大部分的Dialogs.pas
。 / p>
I have an application that uses tabs like the Chrome browser. Now I want to be able to open more forms and not be limited to only one form. These forms should act the same but if I close main form all forms are closed. How can I make all forms be equal, so no matter which form I close it only closes that form and not exit application before all forms are closed? Any ideas?
Kind Regards Roy M Klever
It's not too hard to do this, though it starts getting complicated quickly depending on how complete you want it to be. Getting multiple modal dialogs to work independently is a ton of effort.
To start, you need to avoid Application.MainForm entirely. Always use Form := TMyForm.Create(Application)
instead of Application.CreateForm(TMyForm, Form)
. The later sets MainForm and you never want that to happen.
To make things shut down properly you'll need to do something like this in your form's OnClose
event handler:
if Screen.FormCount = 1 then
Application.Terminate;
CloseAction := caFree;
Application.Run
relies on MainForm being assigned, so in your DPR replace that line with this loop:
repeat
try
Application.HandleMessage;
except
Application.HandleException(Application);
end;
until Application.Terminated;
There are a couple of ways to handle the taskbar entry.
Single taskbar entry: Set
Application.MainFormOnTaskbar := False;
and the hidden TApplication handle will be used. Clicking on the taskbar entry will bring all of the windows to the front. You'll need to overrideApplication.OnMessage
or add aTApplicationEvents
component, and watch forWM_CLOSE
with the Msg.Handle = Application.Handle`. In that case the user has right-clicked on the taskbar and selected Close, so you should close all the windows.Multiple taskbar entries: Set
Application.MainFormOntaskbar := True
. Override your form'sCreateParams
method and setParams.WndParent := 0;
. Each taskbar entry will control that form.
There are probably a few other gotchas, but that's the basics.
As I said, making ShowModal
and TOpenDialog/TSaveDialog
working independently, so it only affects its parent form and so multiple dialogs can be open at once, is a ton of work, and I can't really recommend it. If you're a masochist, here's the general steps:
Replace
TCustomForm.ShowModal
with a custom version. Among other things, that routine disables all the other windows in the application, so you need to replace theDisableTaskWindows/EnableTaskWindows
calls withEnableWindow(Owner.Handle, False/True)
to just disable the parent form. At this point you can open multiple dialogs, but they can only be closed in last-in, first-out order, because the calls end up being recursive. If that's fine, stop here.There are two ways to work around that:
Rather than making
ShowModal
blocking, haveStartModal
andEndModal
routines that have the first bit and last bit of ShowModal's code and call anOnShowModalDone
event when the dialog is closed. This is kind of a pain to use, but is relatively easy to code and easy to make stable.Use the Windows fiber routines to swap out the stack and start a new message loop. This approach is easy to use, because
ShowModal
is blocking, so you call it like normal. This is the approach we use in Beyond Compare. Don't do it. It's complicated to write, and it can get into trouble if there are global message hooks or if any of your code isn't fiber safe.
The common dialogs (TOpenDialog, TColorDialog, etc), have similar restrictions. To make them only disable the parent form you need to override
TCommonDialog.TaskModalDialog
and replace theDisableTaskWindows/EnableTaskWindows
calls there too. They can't be made asynchronous like the regular Delphi dialogs above though, since they're blocking functions provided by Windows (GetOpenFileName, ChooseColor, etc). The only way to allow those to close in any order is to have each dialog run in a dedicated thread. Windows can handle most of the synchronization to do that, as long as you're careful about accessing the VCL objects, but it basically involves rewriting large portions ofDialogs.pas
.
这篇关于Delphi,如何使独立的窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!