It is possible to make a form modal to specific parent form only?
Asked Answered
L

6

12

I've got an application where there's a main background form, from there user can only non-modal forms that maintains different part of the system. The non-modal forms overrides the CreateParams method so each one displays a button in the start task bar:

procedure TfmMaterialsPlanning.CreateParams(var Params: TCreateParams);
begin
   inherited;
   //create a new window on the task bar when this form is created
   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
end;

In effect, a user can open a non-modal form that maintains 'Apples', another non-modal form that maintains 'Oranges', and use the start menu bar to easily switch between the two.

However, if they open a modal form from the 'Apples' form, e.g. to set options, preferences, etc, then they can't use the 'Oranges' forms until they close the modal form.

Is it possible to make a modal form modal to the parent form only? So if they open the Apple's options form, they can't use the Apples maintenance form, but can still use the Oranges maintenance form?

Lengthways answered 21/1, 2009 at 1:10 Comment(0)
S
8

If you have a look at the source code of TCustomForm.ShowModal() you will see that the VCL does not use the Windows API call for showing modal dialogs, but that it does instead disable all other forms in the application while the modal form is shown. You can of course try the very same, just Show() the form-modal dialog, then disable the parent, then re-enable it after the form-modal dialog has been closed. There needs to be a central place where you keep track of form-modal dialogs, the forms that need to be re-enabled and so on. You should however test thoroughly whether the code does indeed what you want it to do, even when switching back and forth between applications, when minimizing the application and so on.

Having said that - I don't think that this a good idea at all. It breaks all assumptions a Windows user makes about the behaviour of applications. Unlike in Mac OS X there is no distinction in Windows between application-modal and form-modal dialogs, and you should stick to the behaviour consistent with the platform your are programming against.

There is most probably a better way to structure your UI. Have a look at the relevant page for dialog boxes in the "Windows User Experience Interaction Guidelines". Modal dialogs are better avoided as much as possible, the linked guidelines show better alternatives for many use cases. If you limit the use of modal dialogs, maybe you do no longer need the form-modal dialogs.

Saxen answered 21/1, 2009 at 8:6 Comment(1)
If the user has two taskbar buttons, it is perfectly within the UI guidelines that a modal dialog shown by one of those two windows does not block the other window. The fact that the two taskbar buttons are hosted by the same process is irrelevant to the user. I would actually argue that one modal dialog blocking both windows inappropriate. Anyway, Daniel links to a solution that works.Hyperbaton
W
3

This post has a nice trick to acomplish your needs: http://blogs.teamb.com/deepakshenoy/2006/08/21/26864

The summary is to reenable the nonmodal window you want when a modal window disabled it.

Windowlight answered 15/9, 2010 at 15:56 Comment(1)
Works great! Probably a good idea to add some extra logic so the form doesn't send WM_REENABLED to itself when it is showing a modal dialog of its own.Hyperbaton
C
2

TeamB website had a solution posted by Deepak Shenoy back in 2006, but the website is down. So, here is a copy of that page:


Yes. Going through the Delphi source code in Forms.pas, in TForm.ShowModal() a call to "DisableThreadWindows" is made to ensure that all non modal forms are inactive, and when the modal form is closed "EnableThreadWindows" is called. What these do is simply disable all other windows and then re-enable them.

What you can do is:

  1. Handle the WM_ENABLE message that is sent to your form to set the enabled state.
  2. Use "PostMessage" to post a custom message (say WM_REENABLE) back to your form.
  3. Handle the WM_REENABLE custom message and in the handler, Enable your window.

So in your Delphi form create a form like so:

type
 TForm2 = class(TForm)
…
   procedure WMEnable(var Message: TWMEnable); message WM_ENABLE;
…

procedure TForm2.WMEnable(var Message: TWMEnable);
begin
 if not Message.Enabled then
   PostMessage(Self.Handle, WM_REENABLE, 1, 0);
end;

where WM_REENABLE is defined earlier as:

const
 WM_REENABLE = WM_USER + $100;

Then add a WM_REENABLE handler like this:

type
 TForm2 = class( TForm )
…
   procedure WMReenable(var Message: TMessage); message WM_REENABLE;
…
procedure TForm2.WMReenable(var Message: TMessage);
begin
  EnableWindow(Self.Handle, True);
end;

Now your form will be active even if a modal window is shown.

Note: I have tried the following and they do not work:

  1. Calling EnableWindow() in the WM_ENABLE handler.
  2. Posting a WM_ENABLE message in the WM_ENABLE handler.
  3. Trying to change Window styles of my window to Stay on Top.

Note: Do not call "SendMessage" instead of PostMessage, it will not work consistently.


But if you ask me, I am 101% on @mghie's side: Don't program against the operating system/platform. Don't do something that is so totally different from what the user (and the OS) expects. Maybe that modal window is not that "modal" as you think. Check why you need to "lock" that window in modal state and probalby that thing, is something you need to change in your architecture.

In hte worst case, maybe you could reparent the log into the modal window.

Consequently answered 28/11, 2023 at 10:6 Comment(0)
R
0

Couldn't you achieve the same effect by preventing the "Apples" form from accepting focus while its child form is open?

Riata answered 21/1, 2009 at 3:10 Comment(0)
H
0

Just as an aside (although it would be an awful lot of work), another approach to this problem is the way Google's chrome went, where each "tab" is a separate process but appear to the user as a single integrated application.

Even though this approach would achieve what you wanted, I would have to agree with the comment above that this would break a user assumptions and expectations about modal behaviour.

Hairraising answered 21/1, 2009 at 9:9 Comment(0)
A
-1

This is possible, if you create each non-modal form in its own thread. Each modal form will then block the thread it belongs to.

Edit: This should be possible, even though the vcl is not thread-safe. Please take a look at Alexeys explanation of how this can be done:

So if you have a set of forms that should live in a separate thread then place them into one dll, compile it without packages and use! It will work and it will be thread-safe.

Arawak answered 21/1, 2009 at 8:2 Comment(3)
Impossible to do when using the VCL, it can be used only from one thread when doing GUI stuff.Saxen
True, but then each DLL has its own copy of the whole VCL and its own Application object. IMO this is hardly better than going with distinct executables in the first place.Saxen
I don't now why Robo want things the way he describes it. He asks if its possible, and I say it is. You say it ain't, I say you're wrong...Arawak

© 2022 - 2024 — McMap. All rights reserved.