Monday, August 21, 2006

Keep a "Non Modal" window active when a modal window is being shown

Recently, I was exploring writing an ADO SQL logging tool (sorta like BDE monitor) - more details on that later - which would show SQL statements on the screen as they ran. All was well until I figured I needed to be able to see my SQL logging form even when a Modal window was being displayed. TForm.ShowModal() disables all other windows in the current thread, so my SQL logging form is there in the background - but that's not much use because I don't want to close my modal form to get to the SQL log.

So is there a way to have another active while a Modal form is being displayed?

The answer is 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
begin
PostMessage(Self.Handle, WM_REENABLE, 1, 0);
end;
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#2: Do not call "SendMessage" instead of PostMessage, it will not work consistently.