Как не иметь MainForm в Delphi? - PullRequest
       33

Как не иметь MainForm в Delphi?

5 голосов
/ 21 октября 2010

Я пытался заставить некоторые приложения немодальной формы отображаться на панели задач - используя новую полезную панель задач в Windows 7.

Существует множество проблем с VCL, которые необходимо устранить, прежде чем форма сможет существовать на панели задач.

Но последняя проблема заключается в том, что сворачивание формы, назначенной VCL для основной формы , приводит к исчезновению всех окон в приложении.

Десять лет назад Питер Белоу (TeamB) задокументировал эти проблемы и попытался их обойти. Но есть некоторые проблемы, которые не могут быть решены. Проблемы настолько глубоки внутри самого VCL, что фактически невозможно заставить приложения Delphi работать должным образом.

Все это связано с тем, что кнопка, которую вы видите на панели инструментов, не представляет окно приложения; оно представляет собой окно TApplications, которое скрыто и никогда не видимо. И затем есть приложение MainForm, которое затем наделено особыми способностями, где, если оно свернуто, то оно инструктирует приложение, чтобы скрыть себя. Мне кажется, что если я смогу сделать

Application.MainForm := nil;

тогда все эти ошибки исчезнут. Приложение может иметь свое скрытое окно, и тем временем я переопределю каждую другую форму в приложении, включая my главную форму, с:

procedure TForm2.CreateParams(var params: TCreateParams ); 
begin 
   inherited CreateParams(params); 
   params.ExStyle := params.ExStyle or WS_EX_APPWINDOW; 
end; 

Но в Delphi свойство Application.MainForm доступно только для чтения.

Как я могу не иметь MainForm в Delphi?

Смотри также

Ответы [ 6 ]

8 голосов
/ 21 октября 2010

Вы не можете запустить проект с графическим интерфейсом без назначенной MainForm.Основной цикл сообщений завершится немедленно, без такового.Однако это не означает, что MainForm должен запускать ваш пользовательский интерфейс.Вы можете использовать пустую скрытую TForm в качестве назначенной MainForm, а затем сделать так, чтобы она создавала вашу реальную MainForm как вторичную TForm.Например:

HiddenMainFormApp.dpr:

project HiddenMainFormApp;

uses
  ..., Forms, HiddenMainForm;

begin
  Application.Initialize;
  Application.CreateForm(THiddenMainForm, MainForm);
  Application.ShowMainForm := False;
  Application.Run;
end.

HiddenMainForm.cpp:

uses
  ..., RealMainForm;

procedure THiddenMainForm.FormCreate(Sender: TObject);
begin
  RealMainForm := TRealMainForm.Create(Self);
  RealMainForm.Show;
end;

RealMainForm.cpp:

procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Application.Terminate;
end;

В качестве альтернативы:

HiddenMainFormApp.dpr:

project HiddenMainFormApp;

uses
  ..., Forms, HiddenMainForm, RealMainForm;

begin
  Application.Initialize;
  Application.CreateForm(THiddenMainForm, MainForm);
  Application.ShowMainForm := False;

  RealMainForm := TRealMainForm.Create(Application);
  RealMainForm.Show;
  RealMainForm.Update;

  Application.Run;
end.

RealMainForm.cpp:

procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Application.Terminate;
end;
5 голосов
/ 21 октября 2010

Вы не можете, особенно в Delphi 5.

Ваша цитата относительно окна TApplication, отображаемого на панели задач, не соответствует действительности для нескольких версий Delphi (я думаю, что D2007 изменил ее).

Поскольку вы используете Delphi 5, вы используете устаревшую копию Delphi; В текущих версиях почти ничего из того, о чем ты пишешь, больше не написано. Я бы посоветовал вам перейти на более позднюю версию Delphi (D5 очень старая); Delphi 2007, если вам нужно избегать Unicode, Delphi XE, если вы можете использовать (или не против) поддержку Unicode в VCL и RTL.

Вещи, которые вы описываете, не ошибок , кстати. Они были преднамеренными проектными решениями, принятыми во время разработки Delphi 1, и благодаря Delphi 7 прекрасно работали с доступными версиями Windows. Изменения в более поздних версиях Windows (XP / Vista / Win7 и эквивалентных версиях сервера) сделали необходимые изменения в этой архитектуре, и они были сделаны по мере развития Delphi вместе с Windows. Потому что вы решили не продвигать свою версию Delphi, чтобы сохранить ее более свежей, не делает вещи, о которых вы пишете, волшебным образом становятся ошибками. : -)

3 голосов
/ 03 июня 2011

Назначение Application.MainForm, по-видимому, не является проблемой для отображения другой немодальной формы на панели задач при минимизации MainForm.

Project1.dpr:

program Project1;

uses
  Forms,
  Windows,
  Unit1 in 'Unit1.pas' {MainForm},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

var
  MainForm: TMainForm;

begin
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  ShowWindow(Application.Handle, SW_HIDE);
  Application.Run;
end.

Unit1.pas:

unit Unit1;

interface

uses
  Windows, Messages, Classes, Controls, Forms, StdCtrls, Unit2;

type
  TMainForm = class(TForm)
    ShowForm2Button: TButton;
    ShowForm2ModalButton: TButton;
    procedure ShowForm2ButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ShowForm2ModalButtonClick(Sender: TObject);
  private
    FForm2: TForm2;
    procedure ApplicationActivate(Sender: TObject);
    procedure Form2Close(Sender: TObject; var Action: TCloseAction);
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Visible := True; //Required only for MainForm, can be set designtime
  Application.OnActivate := ApplicationActivate;
end;

procedure TMainForm.ApplicationActivate(Sender: TObject);
{ Necessary in case of any modal windows dialog or modal Form active }
var
  TopWindow: HWND;
  I: Integer;
begin
  TopWindow := 0;
  for I := 0 to Screen.FormCount - 1 do
  begin
    Screen.Forms[I].BringToFront;
    if fsModal in Screen.Forms[I].FormState then
      TopWindow := Screen.Forms[I].Handle;
  end;
  Application.RestoreTopMosts;
  if TopWindow = 0 then
    Application.BringToFront
  else
    SetForegroundWindow(TopWindow);
end;

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

procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
  if Msg.CmdType = SC_MINIMIZE then
    ShowWindow(Handle, SW_MINIMIZE)
  else
    inherited;
end;

{ Testing code from here }

procedure TMainForm.ShowForm2ButtonClick(Sender: TObject);
begin
  if FForm2 = nil then
  begin
    FForm2 := TForm2.Create(Application); //Or: AOwner = nil, or Self
    FForm2.OnClose := Form2Close;
  end;
  ShowWindow(FForm2.Handle, SW_RESTORE);
  FForm2.BringToFront;
end;

procedure TMainForm.Form2Close(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  FForm2 := nil;
end;

procedure TMainForm.ShowForm2ModalButtonClick(Sender: TObject);
begin
  with TForm2.Create(nil) do
    try
      ShowModal;
    finally
      Free;
    end;
end;

end.

unit2.pas:

unit Unit2;

interface

uses
  Windows, Messages, Classes, Controls, Forms;

type
  TForm2 = class(TForm)
  private
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

implementation

{$R *.dfm}

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

procedure TForm2.WMSysCommand(var Msg: TWMSysCommand);
begin
  if Msg.CmdType = SC_MINIMIZE then
    ShowWindow(Handle, SW_MINIMIZE)
  else
    inherited;
end;

end.

(протестировано с D5 и D7 на XP и Win7.)

(И да, вы можете пометить это как не ответ, потому что это не так: MainForm все еще существует. Но я хотел бы думать, что это отвечает на вопрос, стоящий за вопросом ...)

2 голосов
/ 03 июня 2011

Я не могу говорить за Delphi 5, но в Delphi 7 вы можете определенно бегать без основной формы, если хотите испачкать руки. Я раскрыл многие детали в другом ответе здесь .

Поскольку Delphi 5 не имеет свойства MainFormOnTaskbar, вам нужно сделать следующее в вашем dpr:

// Hide application's taskbar entry
WasVisible := IsWindowVisible(Application.Handle);
if WasVisible then
  ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
  GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
if WasVisible then
  ShowWindow(Application.Handle, SW_SHOW);
// Hide the hidden app window window from the Task Manager's
// "Applications" tab.  Don't change Application.Title since
// it might get read elsewhere.
SetWindowText(Application.Handle, '');

Это будет скрывать окно приложения, и до тех пор, пока вы переопределяете CreateParams своей формы для установки Params.WndParent := 0, у каждого из них будет собственная запись на панели задач. Application.MainForm не назначен, поэтому такие вещи, как переопределение свертывания, не являются проблемой, но вы должны быть осторожны с любым кодом, который предполагает, что MainForm действителен.

0 голосов
/ 03 июня 2011

На самом деле большая часть того, на что вы жалуетесь, - это на самом деле дизайн Windows, а не VCL. Подробнее см. Особенности Windows .

Суть вопроса - собственность владельца, и я имею в виду владельца окон, а не владельца VCL.

Собственное окно скрыто, когда его владелец свернут.

Если вы хотите иметь возможность минимизировать основную форму без скрытия других окон, вам необходимо узнать, как работают собственные окна.

0 голосов
/ 21 октября 2010

Вы можете поместить свои немодальные формы в dll, тогда они действуют в значительной степени самостоятельно.(Если вы не используете экземпляр Application dll при их создании (Application.CreateForm), тогда Application.Mainform в dll равен nil).

Конечно, это может оказаться невозможным в зависимости от того, какие формы могутнужно сделать.

...