Making "Stay-on-top-forms" do want you want

by Sep 8, 2004




Delphi supports this type of forms by providing the FormStyle property for the TForm class. All you have to
do is to set this property to fsStayOnTop. So why did I write this article? Well, there are a few
things to consider:

  1. Using fsStayOnTop causes flickering of the form, because it recreates the window handle.
  2. If you have a Stay-On-Top-Form (ok, we'll call that a SOT form from now on), and then a modal dialog is displayed,
    (part of) the dialog will be hidden by the SOT form.
  3. If you activate another application, the SOT form may even remain on top of that application and usually
    that is not the intent.

So, somehow we'll have to deal with these situations.

  1. fsStayOnTop – First of all, let's have a look at how the FormStyle property works:

procedure TCustomForm.SetFormStyle(Value: TFormStyle);
var
  OldStyle: TFormStyle;
begin
  if FFormStyle <> Value then
  begin
    if (Value = fsMDIChild) and (Position = poDesigned) then
      Position := poDefault;
    if not (csDesigning in ComponentState) then DestroyHandleif ((Value = fsMDIForm) or (OldStyle = fsMDIForm)) and not Ctl3d then
      Color := NormalColor;
    if not (csDesigning in ComponentState) then UpdateControlState;
    if Value = fsMDIChild then Visible := True;
  endend

In the code snippet, I underlined the call to DestroyHandle. This causes the window handle of the
form to be destroyed. If the form is visible, the handle will be needed immediately, which causes the form to
flicker. If you allow the user to switch SOT on and off, it doesn't look too well.
The alternative is to use the WinSDK SetWindowPos routine to change SOT and GetWindowLong to
see if a form is SOT.

  1. Dialogs - The problem with modal dialogs is, that they disable all other windows in the
    application (See the TCustomForm.ShowModal method). So once the dialog is displayed and partially
    or completely hidden by the SOT form, the user can't either move the SOT form or make it not-SOT, because
    it is disabled. So he  has to move the dialog, which is bad for his mood. Delphi offers the TApplication.NormalizeTopMosts
    and TApplication.RestoreTopMosts methods to deal with this, but these are not called by ShowModal,
    so you have to do this every time you activate a modal dialog. Moreover, standard messages (ShowMessage
    function) will still be hidden behind the SOT form even then.
    Personally, I think that the need to use NormalizeTopMosts isn't quite fair. I'd say, that if
    someone creates a problem, he's responsible to solve it too! But here, the SOT form causes the trouble,
    and it expects the modal dialog to solve the problem by using NormalizeTopMosts. And this means,
    you have to include many of those calls, which isn't really good programming practice.
    The real problem here is merely the fact that the SOT window is disabled, as pointed out before. So the
    solution is simple. Each time a window is enabled or disabled, windows sends it a WM_ENABLE message. So
    the SOT form can supply a message handler, that temporarily makes it not-SOT, if it is disabled.
  2. Other applications - In most cases, a form is made SOT to make sure it is on top of other forms
    within the same application. But if another application is activated on top of it, you don't want that SOT
    form to hang around there. This can be solved by writing a message handler for the WM_ACTIVATEAPP message,
    which Windows sends to each window in an application when the application is activated or deactivated.

Now we have the basic ingredients for a form that implements a smooth SOT feature. I wrote a form class called
TStayOnTopForm, based on the principles described above. Apart from implementing SOT, it also offers
the possibility to add an item to the form's system menu (that's the menu you see if you right-click on the
title bar of the form).

The class introduces the following public and members:

Member type Visibility Accessibilty Member name Datatype Description
property published Read/write StayOnTop Boolean The StayOnTop property indicates whether the form should remain on top or not.
property published Read/Write StayOnTopItem Boolean The StayOnTopItem indicates whether an item should be included in the system menu to allow the
user to switch between SOT and not-SOT.
property public Read only StayOnTopSuspended Boolean The StayOnTopSuspended property is true when the form is either disabled, or the application is
not active. In this case the StayOnTop property may still be true, but the form is made not-SOT as long
as StayOnTopSuspended is True. The value of the property is completely controlled by the form
itself.

You can download the source code for this class, as well as a demo program, from Code
Central
. With the
information from this article, the source code for TStayOnTopForm (in unit uStayOnTopForm) should be
understandable.

Article originally contributed by Peter Laman