views:

387

answers:

3

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?

Image of my explorer

Kind Regards Roy M Klever

+9  A: 

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.

  1. 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 override Application.OnMessage or add a TApplicationEvents component, and watch for WM_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.

  2. Multiple taskbar entries: Set Application.MainFormOntaskbar := True. Override your form's CreateParams method and set Params.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:

  1. 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 the DisableTaskWindows/EnableTaskWindows calls with EnableWindow(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:

    1. Rather than making ShowModal blocking, have StartModal and EndModal routines that have the first bit and last bit of ShowModal's code and call an OnShowModalDone 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.

    2. 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.

  2. 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 the DisableTaskWindows/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 of Dialogs.pas.

Craig Peterson
Craig,Thank you for a very good answear. I can see it was a little more complicated than I expected. Maybe time to do a little more thinking. This is good help for me. Thank you for taking your time to explain so thoroughly. My problem will be the modal dialogs, I have done some of this before but not in this setting. Well back into the thinking box.
Roy M Klever
A: 

The first form created in a Delphi application is treated as the main form and the application terminates when this form gets closed. The obvious solution is to have a first form that is not one that gets closed by the user but rather one that is not visible to the user and gets closed only when all other forms have been closed.

I have not tried this, but it should work.

dummzeuch
Yep you are right but when I read Craigs answear I got a few gotchas...
Roy M Klever
+1  A: 

Here is a similar StackOverflow question: Multiple app windows activation not working correctly

In my case I don't try to avoid the MainForm like Craig describes. Instead, I hide the main window and all of my real windows are other non-modal forms. I have been happy with how my application works, but Craig's approach may be simpler.

See my answer on the above question to see code samples for my approach and a few links with good background information.

Mark Elder
Thanks Mark,I think it is starting to get complicated allready. I think I gone try the easiest way first then advance until I reach my goal, but it is good to see a lot of good answears. I need all information I can get.
Roy M Klever