Delphi, как сделать независимые окна - PullRequest
10 голосов
/ 11 апреля 2010

У меня есть приложение, которое использует вкладки, такие как браузер Chrome. Теперь я хочу иметь возможность открывать больше форм и не ограничиваться только одной формой. Эти формы должны действовать одинаково, но если я закрываю основную форму, все формы закрываются. Как я могу сделать все формы равными, так что независимо от того, какую форму я закрываю, она только закрывает эту форму и не закрывает приложение до закрытия всех форм? Есть идеи?

Изображение моего исследователя

С уважением Рой М Клевер

Ответы [ 5 ]

16 голосов
/ 12 апреля 2010

Это не так уж сложно сделать, хотя это начинает быстро усложняться в зависимости от того, насколько полно вы хотите, чтобы это было. Заставить несколько модальных диалогов работать независимо друг от друга - это тонна усилий.

Для начала вам нужно полностью избегать 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;

Существует несколько способов обработки записи на панели задач.

  1. Одиночная запись на панели задач: установите Application.MainFormOnTaskbar := False; и будет использоваться скрытый дескриптор TApplication. При нажатии на запись панели задач все окна будут выведены на передний план. Вам нужно будет переопределить Application.OnMessage или добавить компонент TApplicationEvents и следить за WM_CLOSE с помощью Msg.Handle = Application.Handle`. В этом случае пользователь щелкнул правой кнопкой мыши на панели задач и выбрал Закрыть , поэтому вы должны закрыть все окна.

  2. Несколько записей панели задач: Установите Application.MainFormOntaskbar := True. Переопределите метод CreateParams вашей формы и установите Params.WndParent := 0;. Каждая запись панели задач будет контролировать эту форму.

Возможно, есть еще несколько ошибок, но это основа.


Как я уже сказал, заставить ShowModal и TOpenDialog/TSaveDialog работать независимо, так что это влияет только на его родительскую форму, поэтому несколько диалогов могут быть открыты одновременно, это тонна работы, и я не могу порекомендовать это. Если вы мазохист, вот общие шаги:

  1. Заменить TCustomForm.ShowModal пользовательской версией. Помимо прочего, эта процедура отключает все другие окна в приложении, поэтому вам нужно заменить вызовы DisableTaskWindows/EnableTaskWindows на EnableWindow(Owner.Handle, False/True), чтобы просто отключить родительскую форму. На этом этапе вы можете открыть несколько диалогов, но они могут быть закрыты только в порядке «последний пришел - первый вышел», потому что вызовы заканчиваются рекурсивно. Если это нормально, остановись здесь.

    Есть два способа обойти это:

    1. Вместо того, чтобы делать блокировку ShowModal, используйте подпрограммы StartModal и EndModal, которые имеют первый и последний бит кода ShowModal и вызывают событие OnShowModalDone, когда диалоговое окно закрыто. Это довольно трудная задача, но ее относительно легко закодировать и сделать стабильной.

    2. Используйте Windows волоконно-оптические процедуры , чтобы выгрузить стек и запустить новый цикл обработки сообщений. Этот подход прост в использовании, потому что ShowModal блокирует, так что вы называете это как обычно. Этот подход мы использовали в Beyond Compare. Не делайте этого. Сложно написать, там будет проблемы со стабильностью для нетривиальных приложений из-за несовместимости с сторонним кодом (глобальные перехватчики сообщений Windows, TWebBrowser, .NET в расширениях оболочки, загружаемых диалоговым окном просмотра и т. д.), и если это кроссплатформенный проект, функции ucontext в Unix также небезопасны.

  2. Общие диалоги (TOpenDialog, TColorDialog и т. Д.) Имеют аналогичные ограничения. Чтобы они отключили только родительскую форму, вам нужно переопределить TCommonDialog.TaskModalDialog и заменить там тоже DisableTaskWindows/EnableTaskWindows. Однако их нельзя сделать асинхронными, как в обычных диалоговых окнах Delphi выше, поскольку они блокируют функции, предоставляемые Windows ( GetOpenFileName , ChooseColor и т. Д.). Единственный способ разрешить их закрывать в любом порядке - это запускать каждый диалог в отдельном потоке. Windows может справиться с большей частью синхронизации, если вы будете осторожны с доступом к объектам VCL, но в основном это связано с переписыванием больших частей Dialogs.pas.

2 голосов
/ 12 апреля 2010

Вот аналогичный вопрос StackOverflow: Неправильная активация нескольких окон приложений

В моем случае я не пытаюсь избегать MainForm, как описывает Крейг. Вместо этого я скрываю главное окно, и все мои настоящие окна являются другими немодальными формами. Я был доволен тем, как работает мое приложение, но подход Крейга может быть проще.

См. Мой ответ на вышеуказанный вопрос, чтобы увидеть примеры кода для моего подхода и несколько ссылок с хорошей справочной информацией.

2 голосов
/ 11 апреля 2010

Если вы действительно этого хотите,

1) используйте небольшую, возможно скрытую, MainForm и запускайте только первую дочернюю форму при запуске.

2) запускать отдельные приложения вместо Windows в одном процессе.Это то, что позже использовать версию Office.

1 голос
/ 12 апреля 2010

Первая форма, созданная в приложении Delphi, рассматривается как основная форма, и приложение закрывается, когда эта форма закрывается. Очевидное решение состоит в том, чтобы иметь первую форму, которая не является закрытой пользователем, а скорее невидимой для пользователя и закрытой только тогда, когда все другие формы были закрыты.

Я не пробовал это, но оно должно работать.

0 голосов
/ 01 июня 2017

Это слишком поздно, чтобы быть ответом, но я столкнулся с той же проблемой. Решение, которое я выбрал, состоит в том, чтобы извлечь Application.ExeName и передать его функции, подобной createProcess или даже shellExecute. Итак, теперь у меня есть независимые приложения на уровне ОС. Мне также нужны были разные кнопки панели задач для разных экземпляров.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...