Это не так уж сложно сделать, хотя это начинает быстро усложняться в зависимости от того, насколько полно вы хотите, чтобы это было. Заставить несколько модальных диалогов работать независимо друг от друга - это тонна усилий.
Для начала вам нужно полностью избегать Application.MainForm. Всегда используйте Form := TMyForm.Create(Application)
вместо Application.CreateForm(TMyForm, Form)
. Последний устанавливает MainForm, и вы никогда не хотите, чтобы это произошло.
Чтобы правильно завершить работу, вам нужно сделать что-то подобное в обработчике событий OnClose
формы:
if Screen.FormCount = 1 then
Application.Terminate;
CloseAction := caFree;
Application.Run
зависит от назначения MainForm, поэтому в вашем DPR замените эту строку следующим циклом:
repeat
try
Application.HandleMessage;
except
Application.HandleException(Application);
end;
until Application.Terminated;
Существует несколько способов обработки записи на панели задач.
Одиночная запись на панели задач: установите Application.MainFormOnTaskbar := False;
и будет использоваться скрытый дескриптор TApplication. При нажатии на запись панели задач все окна будут выведены на передний план. Вам нужно будет переопределить Application.OnMessage
или добавить компонент TApplicationEvents
и следить за WM_CLOSE
с помощью Msg.Handle = Application.Handle`. В этом случае пользователь щелкнул правой кнопкой мыши на панели задач и выбрал Закрыть , поэтому вы должны закрыть все окна.
Несколько записей панели задач: Установите Application.MainFormOntaskbar := True
. Переопределите метод CreateParams
вашей формы и установите Params.WndParent := 0;
. Каждая запись панели задач будет контролировать эту форму.
Возможно, есть еще несколько ошибок, но это основа.
Как я уже сказал, заставить ShowModal
и TOpenDialog/TSaveDialog
работать независимо, так что это влияет только на его родительскую форму, поэтому несколько диалогов могут быть открыты одновременно, это тонна работы, и я не могу порекомендовать это. Если вы мазохист, вот общие шаги:
Заменить TCustomForm.ShowModal
пользовательской версией. Помимо прочего, эта процедура отключает все другие окна в приложении, поэтому вам нужно заменить вызовы DisableTaskWindows/EnableTaskWindows
на EnableWindow(Owner.Handle, False/True)
, чтобы просто отключить родительскую форму. На этом этапе вы можете открыть несколько диалогов, но они могут быть закрыты только в порядке «последний пришел - первый вышел», потому что вызовы заканчиваются рекурсивно. Если это нормально, остановись здесь.
Есть два способа обойти это:
Вместо того, чтобы делать блокировку ShowModal
, используйте подпрограммы StartModal
и EndModal
, которые имеют первый и последний бит кода ShowModal и вызывают событие OnShowModalDone
, когда диалоговое окно закрыто. Это довольно трудная задача, но ее относительно легко закодировать и сделать стабильной.
Используйте Windows волоконно-оптические процедуры , чтобы выгрузить стек и запустить новый цикл обработки сообщений. Этот подход прост в использовании, потому что ShowModal
блокирует, так что вы называете это как обычно. Этот подход мы использовали в Beyond Compare. Не делайте этого. Сложно написать, там будет проблемы со стабильностью для нетривиальных приложений из-за несовместимости с сторонним кодом (глобальные перехватчики сообщений Windows, TWebBrowser, .NET в расширениях оболочки, загружаемых диалоговым окном просмотра и т. д.), и если это кроссплатформенный проект, функции ucontext в Unix также небезопасны.
Общие диалоги (TOpenDialog, TColorDialog и т. Д.) Имеют аналогичные ограничения. Чтобы они отключили только родительскую форму, вам нужно переопределить TCommonDialog.TaskModalDialog
и заменить там тоже DisableTaskWindows/EnableTaskWindows
. Однако их нельзя сделать асинхронными, как в обычных диалоговых окнах Delphi выше, поскольку они блокируют функции, предоставляемые Windows ( GetOpenFileName , ChooseColor и т. Д.). Единственный способ разрешить их закрывать в любом порядке - это запускать каждый диалог в отдельном потоке. Windows может справиться с большей частью синхронизации, если вы будете осторожны с доступом к объектам VCL, но в основном это связано с переписыванием больших частей Dialogs.pas
.