How Can I Keep the FindDialog from Staying on Top (Delphi)?
Asked Answered
B

1

7

In Delphi 2009, I do a simple:

FindDialog.Execute;

The FindDialog window stays on top of my program's main window as it should.

However, if I open another window from some other program over my own program's window, the FindDialog window remains on top of the other window.

If I try this with a FindDialog from another program (e.g. Notepad), this does not happen. Opening another program's window over Notepad and its FindDialog will cover both the Notepad and FindDialog windows. This seems to be the correct and expected behavior.

Is this something I'm doing wrong or is this a problem with the way Delphi has implemented the FindDialog? Is there something I can do to make it work the Notepad way?


Thank you all for the comments. The fact that you cannot reproduce my problem is already a clue that it is something else causing this. This will help me track it down. I'll research a little more and post additional info here when I find out something.


Very interesting. My PrintDialog doesn't stay on top. Still don't know why my FindDialog does. Still researching...


I changed the call to: FindDialog.Execute(Handle); Still on top.


I added another FindDialog (this time FindDialog1) to my main form and I execute it at startup of my program. It has the same stay-on-top behavior. That at least indicates it wasn't anything to do with my FindDialog or customizations I made to do with it. So it must be a setting in my main form.


It doesn't look like I'm the only one who's encountered this. See: Resource Tuner: Version History which appears to be a Delphi application, where under Version 1.99 it states: "Bugfix: The (search) dialog preview window stayed on top when switching to another application." I might try contacting them and see if they might remember what their fix was.


I add some new dialogs to my form and put these calls in one place:

FindDialog1.Execute();
PrintDialog1.Execute();
ReplaceDialog1.Execute();
FontDialog1.Execute();

The FindDialog and ReplaceDialog stay on top in front of other windows. The PrintDialog and FontDialog do not stay on top and work as they should.

So what is different between the two sets of dialogs that make the first two do it wrong?


Also, this problem happens in an old version of my program that was compiled with Delphi 4. Whoops. Now I see this problem did not happen in my old version that used Delphi 4.

And it was a user who reported this problem. He uses Windows XP, and I'm developing on Vista, so it happens under different OS's.


Confirmation: Yes, I create a new form and add a FindDialog on it. The FindDialog does NOT have the problem. This indicates something in my program is causing the FindDialog to stay on top. Now, I've just got to find out what that is. Any more ideas? If someone gives me an answer that even gives me a clue to help me solve this, then they'll get the accepted answer.


Solution: Sertac's edit to his answer gave me the workaround:

  Application.NormalizeTopMosts;
  FindDialog.Execute();
  Application.RestoreTopMosts;

Doing this prevents the FindDialog from being TopMost when the Application is not TopMost.

... But I still really don't understand this (the Delphi help on NormalizeTopMosts) is very confusing and doesn't indicate that it should do this.

Hopefully this "fix" won't cause other problems.

Blas answered 21/3, 2011 at 4:52 Comment(9)
Can't reproduce this (D2009 fully updated). Used both FindDialog1.Execute; and FindDialog1.Execute(); which should both lead to no handle passed in. When I open the Find dialog, then open some other app (Notepad in my case) and move it over my Delphi app with dialog, both its main window and the find dialog are covered by Notepad's window.Snowy
I might have misunderstood something here but I can't reproduce in Delphi XE.Tadtada
FWIW I can't reproduce this behaviour in D2010. Are you passing an HWND to the Execute method? If not try passing the handle of the main form and see if that helps.Immunogenetics
Please post a small compiling sample that demonstrates this anomaly.Markman
Are you using the overloaded FindDialog.Execute that accepts a window handle as a parameter? If so, what handle are you passing? If you're passing anything other than your Form's Handle (the form that creates it), this can be the problem. If you're passing nothing now, try passing your MainForm.Handle.Admiration
I also use Delphi 2009, but cannot reproduce this no matter how much I try to abuse the component (e.g. by passing GetDesktopWindow as the argument!).Honoria
@all - Looking at the code in D2007, the ParentWnd that's passed to the overloaded FindDialog.Execute() does nothing, it is not referred to even once. The hwndOwner of a Find Dialog Box is always some TRedirectorWindow. EnumThreadWindows is used to find the 'Top Window' which then is passed to become the 'redirector's window owner, which is the Find Dialog's window owner. Always!. See the code and the comment '{ TRedirectorWindow }' in dialogs.pas.Fiann
... not that I was able to duplicate the problem, but with all the talk about the overload just wanted to say..Fiann
There's no reason the print dialog or the font dialog would behave the same. They don't share the 'Execute' code. When application's ModalPopupMode is pmNone, main form's handle or the application's window's handle becomes the owner of these dialogs depending on 'MainFormOnTaksbar's setting. When ModalPopupMode is not pmNone, the handle passed to Execute, or the derived one if one is not passed, is honored that they 'own' a RedirectorWindow which owns the dialog.Fiann
F
3

Looking at the VCL code, the only possible way a Find Dialog Box stays on top is, there's already a top-most window when 'Execute' is called. This is how it is coded, the dialog gets owned by a 'TRedirectorWindow' which gets owned by the top window in z-order in the application. If this 'top window' is a top-most window then the find dialog also is.

procedure TForm1.Button1Click(Sender: TObject);
var
  f: TForm;
begin
  f := TForm.CreateNew(Self);
  f.FormStyle := fsStayOnTop;
  f.Show;
  FindDialog1.Execute;
end;

or,

procedure TForm1.Button1Click(Sender: TObject);
begin
  FormStyle := fsStayOnTop;
  FindDialog1.Execute;
  FormStyle := fsNormal;
end;


The above samples will create a top-most find dialog. But a stay-on-top form possibly wouldn't go unnoticed, so I guess this wouldn't be the source of your problem.

In any case, it is either that or you're somehow changing the styles on the dialog by some other code piece.


BTW, do not bother testing passing various handles to FindDialog1.Execute(), it won't have an effect, see my comment to your question.

edit:

How about this one:

procedure TForm1.Button4Click(Sender: TObject);
var
  f: TForm;
begin
  f := TForm.CreateNew(Self);
  f.FormStyle := fsStayOnTop;
  f.Show;
  f.Hide;
  FindDialog1.Execute;
end;

The point is, a window does not have to be visible to get enumerated by EnumThreadWindows. So any existing stay-on-top form could cause the find dialog to exhibit this behavior.

Better test and see than to guess. Run the below test just before you launch your Find Dialog. This incorporates the logic 'dialogs.pas' performs to find the dialog a base, and would raise an exception if the dialog would go top-most.

function EnumThreadWndProc(hwnd: HWND; var lParam: LPARAM): Bool; stdcall;
var
  Window: TWinControl;
begin
  Result := True;
  Window := FindControl(hwnd);
  if Assigned(Window) and (Window is TForm) then begin
    Result := False;
    lParam := Longint(Window);
  end;
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  OnTopForm: Longint;
begin
  OnTopForm := 0;
  EnumThreadWindows(GetCurrentThreadId, @EnumThreadWndProc, LPARAM(@OnTopForm));
//  if (OnTopForm <> 0) and (TForm(OnTopForm).FormStyle = fsStayOnTop) then
  if (OnTopForm <> 0) and (GetWindowLong(TForm(OnTopForm).Handle,
                            GWL_EXSTYLE) and WS_EX_TOPMOST = WS_EX_TOPMOST) then
    raise Exception.Create('darn! got one: ' + TForm(OnTopForm).Name);
end;


One other test could be to call NormalizeTopMosts of the Application before launching the dialog, but I know with some Delphi versions this method is broken and does not do its job.

Fiann answered 22/3, 2011 at 2:0 Comment(4)
Hmmm. I do have 3 forms that are FormStyle := fsStayOnTop, but these are special function forms (like my About box) that are not opened yet when this problem occurs. Your answer makes me think, though. Thanks.Blas
NormalizeTopMosts seems to solve it for me. See my edited answer. Thanks Sertac!!Blas
@Blas - You're welcome! I don't think 'NormalizeTopMosts' will cause any problems, the normalization will last a very small time frame.., don't forget to protect the 'RestoreTopMosts' in a 'finally'. What I would do for a production application is to probably modify dialogs.pas to get rid of the nonsensical code and make the overload work, but of course, that may not be feasible at all (packages, company policy, etc..).Fiann
See "Solving a Bug when you have No Idea What’s Causing It" at beholdgenealogy.com/blog/?p=812Blas

© 2022 - 2024 — McMap. All rights reserved.