Link to home
Start Free TrialLog in
Avatar of tzigi
tzigi

asked on

How to Show a Child Form while the Application is Minimized

Greetings,
I have a program that on an event it shows a form.
If I minimize the Application, the form will not show until I click the Application Title in the taskbar.
Can I show or keep a child form visible while the Application is minimized ?
Avatar of Ivanov_G
Ivanov_G
Flag of Bulgaria image

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses Unit2;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Application.Minimize;
  Form2 := TForm2.Create(nil);
  Form2.FormStyle := fsStayOnTop;
  Form2.ShowModal;
end;

end.



// NOTE : remove Form2 from Auto-Create Forms in Project / Options / Forms
Avatar of shaneholmes
shaneholmes

to add to Ivanov_G,

If you assign your own event to the global application, then no matter where you minimize from in the application,
the event will be called.....


Application.OnMinimize := AppMinimize;


procedure AppMinimize;
begin
  Form2 := TForm2.Create(nil);
  Form2.FormStyle := fsStayOnTop;
  Form2.ShowModal;
end;

// NOTE : remove Form2 from Auto-Create Forms in Project / Options / Forms

SHane

Hello

If you are using windows 2000 then showing the form on a minimized application becomes tricky, Windows 98/2000 doesn't want to foreground a window when some other window has keyboard focus, so even if you restore your form with the methods described earlier in my own experince they still would remind in the background of the currently active application. So unless your desktop was totally minimized it wouldnt work the way you want it to, somehow fsStayOnTop doesnt help either...

I found the following procedure to achive total frontness on all systems

I cant take the credit as i found it as a snipet long time ago, the original author did a great job though

function ForceForeground(AppHandle:HWND): boolean;
const
SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
ForegroundThreadID: DWORD;
ThisThreadID      : DWORD;
timeout           : DWORD;
OSVersionInfo     : TOSVersionInfo;
Win32Platform     : Integer;
begin
if IsIconic(AppHandle) then ShowWindow(AppHandle, SW_RESTORE);
if (GetForegroundWindow = AppHandle) then Result := true else
begin
Win32Platform := 0;
OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
if GetVersionEx(OSVersionInfo) then Win32Platform := OSVersionInfo.dwPlatformId;

{ Windows 98/2000 doesn't want to foreground a window when some other window has keyboard focus}

if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (OSVersionInfo.dwMajorVersion > 4)) or
   ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and ((OSVersionInfo.dwMajorVersion > 4) or
   ((OSVersionInfo.dwMajorVersion = 4) and (OSVersionInfo.dwMinorVersion > 0)))) then
begin
  Result := false;
  ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow,nil);
  ThisThreadID := GetWindowThreadPRocessId(AppHandle,nil);
  if AttachThreadInput(ThisThreadID, ForegroundThreadID, true) then
  begin
    BringWindowToTop(AppHandle);
    SetForegroundWindow(AppHandle);
    AttachThreadInput(ThisThreadID, ForegroundThreadID, false);
    Result := (GetForegroundWindow = AppHandle);
  end;
  if not Result then
  begin
    SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
    SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0), SPIF_SENDCHANGE);
    BringWindowToTop(AppHandle);
    SetForegroundWindow(AppHandle);
    SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
    Result := (GetForegroundWindow = AppHandle);
    if not Result then
      begin
      ShowWindow(AppHandle,SW_HIDE);
      ShowWindow(AppHandle,SW_SHOWMINIMIZED);
      ShowWindow(AppHandle,SW_SHOWNORMAL);
      BringWindowToTop(AppHandle);
      SetForegroundWindow(AppHandle);
      end;
  end;
end else
begin
  BringWindowToTop(AppHandle);
  SetForegroundWindow(AppHandle);
end;
Result := (GetForegroundWindow = AppHandle);
end;
end;

You would still have to create the form using

  Form2 := TForm2.Create(Application);
  Form2.ShowModal;
  Form2.Free;

and in the create event of form2 call the function about to put your form on top of everything else

ForceForeground(Form2.Handle); or just  ForceForeground(Handle);

also if you use form2.show and create form2 automatically then i guess you can just call ForceForeground(Form2.Handle); when your special event occurs.

Ekim
Avatar of tzigi

ASKER

Greetings,
The suggested sollutions work.
 The only problem now is that if I MANUALLY minimize Form1, Form2 will not remain on screen.
I cannot use (of course) showmodal, cause then I will not be able to minimize Form1.
Regards,
 Tzigi

   handle WM_SYSCOMMAND with wParam = SC_MINIMIZE. Thus you will know when Form1 is minimized and you can do your stuff there

----------------------------

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)
  private
    { Private declarations }
    procedure OnMinimize (var Msg : TMessage); message WM_SYSCOMMAND;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{ TForm1 }

procedure TForm1.OnMinimize(var Msg: TMessage);
begin
  if Msg.WParam = SC_MINIMIZE then
    ShowMessage('Minimize');
end;

end.
hello  tzigi. , , I did a program where I wanted a small information form, to pop up (show) while my programm was minimized,
but no matter what I tried, no Forms will show when the app is minimized, this is not a delphi thing, this is a windows system (API)  thing. When a program is minimized, the system will not allow any windows (forms) associated with the program (TApplication in the case of delphi) to show, even if you set them to visible. So, for another program , , where I wanted persistent forms (not hide for minimized) I had to go a different way, I wish it was as easy as  Ivanov_G  says, but it was not for me. I had to catch the Forms syscommand and the application's syscommand stuff and some other messages as well

Some code - -

first the dpr file

program DllFrame;

uses
  Forms,
  DllFrame1 in 'DllFrame1.pas' {Form1},
  SecondForm in 'SecondForm.pas' {Form2};

{$R *.RES}

begin
  Application.Initialize;
  Application.Title := 'Frame Dll';
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.

 - - - - - - - - - -  - - - - - -
you WILL NOT need to change the DPR program file at all, , , , ,  
I include this just to show you that I Create the Form2 from the start, not later.


 - - - - - - - - - - - - - - - - - - -


I have a TApplicationEvents on the main  form

  CODE FOR  Main  Form1  - - -

  TForm1 = class(TForm)
    ApplicationEvents1: TApplicationEvents;
    procedure FormCreate(Sender: TObject);
    procedure ApplicationEvents1Message(var Msg: tagMSG;
      var Handled: Boolean);
  private
    { Private declarations }
    procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;


var
  Form1: TForm1;

implementation

{$R *.DFM}

uses SecondForm; // include you Form2 unit here



procedure TForm1.FormCreate(Sender: TObject);
begin
{ the sys menu ID should NOT have any $F bits in it, , I use
  $F0, but you can use $F10 to $F0, do not use the last 0 of the Hex, let it stay 0. .
   this is created grayed, disabled, in the top position}
InsertMenu(GetSystemMenu(Application.Handle, False), 0,
           MF_BYPOSITION or MF_STRING or MF_GRAYED, $F0, 'Show Main Window');
                              // change 'Show Main Window' to your own menu text
end;



procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
begin
Handled := False;
{you will need to get the Application's WM_SYSCOMMAND message}
if Msg.hwnd = Application.Handle then
  if (Msg.message = WM_SYSCOMMAND) then
    if (Msg.wParam and $FFF0) = $F0 then //$F0 is your menu Command ID
      begin
      if IsIconic(Application.Handle) then
        Application.Restore;
      Show;  // Main Form will show
      EnableMenuItem(GetSystemMenu(Application.Handle, False), $F0, MF_BYCOMMAND or MF_GRAYED);
      end else
      if ((Msg.wParam and $FFF0) = SC_MINIMIZE) and  Visible then
        begin
        { I'm NOT sure this is the functionallity you would want? ?
          you may want the TaskBar or System Menu to really Minimize?
          if so, , then leave out this   Msg.wParam and $FFF0) = SC_MINIMIZE  test}
        EnableMenuItem(GetSystemMenu(Application.Handle, False), $F0, MF_BYCOMMAND or MF_ENABLED);
        Hide;  // Main Form will hide
        Handled := True;
        Exit;
        end;
end;



procedure TForm1.WMSysCommand(var Message: TWMSysCommand);
begin
{you will need to Hide main form instead of minimize app, if I minimize the app,
then I CAN NOT show any form at all}
with Message do
  if (CmdType and $FFF0 = SC_MINIMIZE) and Assigned(Form2) and Form2.Visible then
    begin
    EnableMenuItem(GetSystemMenu(Application.Handle, False), $F0, MF_BYCOMMAND or MF_ENABLED);
    Hide;  // hide this main Form
    end else
    inherited;
end;



 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

CODE FOR Form2


type
  TForm2 = class(TForm)

  private
    { Private declarations }
    procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;



var
  Form2: TForm2;

implementation

{$R *.DFM}

uses DllFrame1; // Main Form1 unit


procedure TForm2.WMSysCommand(var Message: TWMSysCommand);
begin
{if Form 2 is the Only form visible, I would change the
  syscommand}
with Message do
  begin
  if (Assigned(Form1)) and Form1.Visible then
    begin
    {if Form1 is up then do nothing}
    inherited;
    Exit;
    end;

  if (CmdType and $FFF0 = SC_MINIMIZE) then
    begin
    Application.Minimize;
   { you may want different action here, but this is what I needed}
    end else
    if (CmdType and $FFF0 = SC_CLOSE) then
      Application.Terminate  // Form2 as the Main Form, close Application
      else inherited;
    end;
end;



 = = = = = = = = = = = = = = = = = = = = = = = = = =  ==

this did what I wanted to do, but this takes it OUT of the usuall delphi methods, so some other things (show and hide?) may not do as normal delphi would do

ask questions if you need more info
ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of tzigi

ASKER

Slick812, you have earned your points.

Just one clarification, can you repost the fixed procedure TForm2.WMSysCommand(var Message: TWMSysCommand); ?
Thanks a lot !,
 Tzigi
I was look at my post to check it and thought I saw a misteak? but I lost count of the begin - - end; combinations and posted a correction

but the correction is Incorrect ! ! !   Duhhhhhhhhhhhhhhhhhhhh !

I shoud be sleeping instead of coding, :-)  Ha ha

so the original code is correct and the new correction code is not needed