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

Written by Embarcadero USA on Posted in PROGRAMMING

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

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 DestroyHandle;
    OldStyle := FFormStyle;
    FFormStyle := Value;
    if ((Value = fsMDIForm) or (OldStyle = fsMDIForm)) and not Ctl3d then
      Color := NormalColor;
    if not (csDesigning in ComponentState) then UpdateControlState;
    if Value = fsMDIChild then Visible := True;
  end;
end;

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

Tags: Delphi Platforms Programming Win32 Higher Education



Check out more tips and tricks in this development video: