Question

I have a Delphi application that has a document browser as the main form. When the user opens a document, we open an editor window. We want to have each editor with a button on the task bar, as well as the main form. I've applied the normal code to do this (below), but when I click on the main form after using the editor window the editor is being left on top, while the focus is on the main form. I'm unable to work out what is causing this behaviour.

Stage setting: I open the main form and a document form.

  1. Click on another app, click on main form, main form stays focused. (Behaving as expected.)

  2. Click on the document form, click on main form, document form comes back to front, but shown inactive. (Picture shows result)

alt text http://www.matthew-jones.com/temp_xfer/titlebarfailure.jpg

First step, this is Delphi 2007, and I have in the project:

Application.MainFormOnTaskBar := True;

For the main form, I have no additional code.

For the document form, I have

procedure TCommonEditForm.CreateParams(var params: TCreateParams);
begin
  inherited;
  params.WndParent := 0; // GetDeskTopWindow; no diff
end;

I've tried to work out if there is a message that is making this happen, but can't locate anything appropriate. I've searched the code for anything to do with "activate". Clues welcome!

Was it helpful?

Solution

My application works in the way you describe. Here is the approach I took. I would have liked to find a simpler approach but never did.

I started out by reading these articles. This first one is an great write up by Peter Below:

http://groups-beta.google.com/group/borland.public.delphi.winapi/msg/e9f75ff48ce960eb?hl=en

Other information was also found here, however this did not prove to be a valid solution: for my use: http://blogs.teamb.com/DeepakShenoy/archive/2005/04/26/4050.aspx

Eventually here is what I ended up with.

My splash screen doubles as the Application Main form. The Main form has a special tie to the Application Object. Using all secondary forms gets me the behavior that I was looking for.

In each form that I want on the task bar I override CreateParams. I do this on my edit forms and what the users sees as the "main form"

procedure TUaarSalesMain.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
  Params.WndParent := GetDesktopWindow;
end;

My "Main" form as far as Delphi is concerned loads the true main form in its Activitate function. I use a member variable to keep track of the first activate. Then at the end of the function I hide the splash form, but do not close it. This was important for me because if the user was editing a document and closed the main form I did not want the edit screens to be forced closed at the same time. This way all of the visible forms are treated the same.

    if FFirstActivate = false then
      exit;

    FFristActivate := false;

    /* 
       Main Load code here 
       Update Splash label, repaint
       Application.CreateForm
       etc.
    */


    // I can't change visible here but I can change the size of the window
    Self.Height := 0;
    Self.Width := 0;
    Self.Enabled := false;

    //  It is tempting to set Self.Visible := false here but that is not
    // possible because you can't change the Visible status inside this
    // function.  So we need to send a message instead.
    ShowWindow(Self.Handle, SW_HIDE);

  end;

But there is still a problem. You need the main/splash window to close when all other forms are closed. I have an extra check in my close routines for Parent <> nil because I use forms as plugins (form my purposes they work better than frames).

I didn't really like using the Idle event, but I don't notice this being a drag on the CPU.

{
  TApplicationManager.ApplicationEventsIdle
  ---------------------------------------------------------------------------
}
procedure TApplicationManager.ApplicationEventsIdle(Sender: TObject;
  var Done: Boolean);
begin

  if Screen.FormCount < 2 then
    Close;
end;

{
  TApplicationManager.FormCloseQuery
  ---------------------------------------------------------------------------
}
procedure TApplicationManager.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
var
  i: integer;
begin

  for i := 0 to Screen.FormCount - 1 do
  begin
    if Screen.Forms[i] <> self then
    begin
      // Forms that have a parent will be cleaned up by that parent so
      // ignore them here and only attempt to close the parent forms
      if Screen.Forms[i].Parent = nil then
      begin
        if Screen.Forms[i].CloseQuery = false then
        begin
          CanClose := false;
          break;
        end;
      end;
    end;
  end;

end;

{
  TApplicationManager.FormClose
  ---------------------------------------------------------------------------
}
procedure TApplicationManager.FormClose(Sender: TObject;
  var Action: TCloseAction);
var
  i: integer;
begin

  for i := Screen.FormCount - 1 downto 0 do
  begin
    if Screen.Forms[i] <> self then
    begin
      // Forms that have a parent will be cleaned up by that parent so
      // ignore them here and only attempt to close the parent forms
      if Screen.Forms[i].Parent = nil then
      begin
        Screen.Forms[i].Close;
      end;
    end;
  end;

end;

This has served me well so far. I did make a small change for Vista because the icon for my "Main/Splash" screen was still showing. I don't remember what that was though. I probably don't need to set width, height, enabled, and send the hide message on the splash screen. I just wanted to make sure it didn't show up :-).

Dealing with the close events was necessary. If I remember correctly that was needed for when windows sent a shutdown message. I think only the main form gets that message.

OTHER TIPS

Sorry if this is really stupid, but you don't have the formstyle set to fsStayOnTop do you? This would explain this behaviour.

perhaps add this in the createparams

Params.ExStyle := Params.ExStyle OR WS_EX_APPWINDOW;

or try this anywhere in the code. I presonally use it on the forms .OnCreate event.

SetWindowLong(Wnd, GWL_EXSTYLE, 
  GetWindowLong(Wnd, GWL_EXSTYLE) or WS_EX_APPWINDOW) ;

the downside of this is that if the main form is minimized the other forms hide aswell, but restore when the main form does.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top