В этой статье речь пойдет об организации диалоговых окон в Delphi, которые обычно вызываются процедурой ShowMessage и подобными стандартными процедурами. При этом вы получаете стандартное окно такого типа. Например, если вызвать процедуру:
ShowMessage('Это стандартное системно окно сообщения');
При этом мы получим вот такие окно:

Как видите это не очень красивое окно. Его смысл в том, чтобы что-то быстро написать на коленке и не более того. Хотя оно модальное и полностью выполняет свою функцию, но это не то, что хотело бы видеть большинство программистов и пользователей программ.
Давайте сделаем свое окно. Я решил сделать четыре типа окна:
- окно с вопросом для пользователя;
- окно с сообщением для пользователя;
- окно с предложением сохранить измененные на форме данные;
- окно, предупреждающее о том, что производится попытка удаления данных.
Окно с вопросом для пользователя
Обычно в таком окне задается какой-либо вопрос, требующий подтверждения или отказа. Например, такое окно может выглядеть так:

Может выглядеть и по другому. Все зависит от оформления. Сами кнопки могут называться как «Да/Нет» или «Принять/Отменить» и т.п.
В кнопке «Да» напишем простой код:
procedure TfrmQuestion.cmdYesClick(Sender: TObject);
begin
bModal:=true;
Close;
end;
В кнопке «Отмена»:
procedure TfrmQuestion.cmdCancelClick(Sender: TObject);
begin
bModal:=false;
Close;
end;
И в событии onFromClose:
procedure TfrmQuestion.FormClose(Sender: TObject; var Action: TCloseAction); begin Action:=TCloseAction.caFree; frmQuestion:=nil; end;
Саму форму назовем frmQuestion. Полный код формы:
unit question_;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Effects, FMX.Objects;
type
TfrmQuestion = class(TForm)
cmdCancel: TButton;
lblMessage: TLabel;
cmdYes: TButton;
StyleBook1: TStyleBook;
recClient: TRectangle;
ShadowEffect1: TShadowEffect;
procedure cmdCancelClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure cmdYesClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmQuestion: TfrmQuestion;
implementation
{$R *.fmx}
uses mdi;
procedure TfrmQuestion.cmdCancelClick(Sender: TObject);
begin
bModal:=false;
Close;
end;
procedure TfrmQuestion.cmdYesClick(Sender: TObject);
begin
bModal:=true;
Close;
end;
procedure TfrmQuestion.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=TCloseAction.caFree;
frmQuestion:=nil;
end;
end.
Переменная bModal — это обычная глобальная переменная типа boolean, которую можно объявить в главной форме приложения.
Окно с сообщением для пользователя
Аналогичным образом создается окно с сообщением для пользователя. Назовем его frmMessage:

Полный код данного окна:
unit message_;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects, FMX.Effects;
type
TfrmMessage = class(TForm)
cmdClose: TButton;
lblMessage: TLabel;
StyleBook1: TStyleBook;
recClient: TRectangle;
ShadowEffect1: TShadowEffect;
procedure cmdCloseClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMessage: TfrmMessage;
implementation
{$R *.fmx}
procedure TfrmMessage.cmdCloseClick(Sender: TObject);
begin
Close;
end;
procedure TfrmMessage.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=TCloseAction.caFree;
frmMessage:=nil;
end;
end.
Как видим, здесь всего три строчки кода, не считая автоматически сгенерированного средой Delphi.
Окно с предложением сохранить измененные на форме данные
Данное окно назовем frmPost:

Код окна:
unit post_;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Effects, FMX.Objects;
type
TfrmPost = class(TForm)
cmdCancel: TButton;
lblMessage: TLabel;
cmdYes: TButton;
cmdNo: TButton;
StyleBook1: TStyleBook;
recClient: TRectangle;
ShadowEffect1: TShadowEffect;
procedure cmdCancelClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure cmdYesClick(Sender: TObject);
procedure cmdNoClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmPost: TfrmPost;
implementation
{$R *.fmx}
uses mdi;
procedure TfrmPost.cmdCancelClick(Sender: TObject);
begin
sPost:='Отмена';
Close;
end;
procedure TfrmPost.cmdNoClick(Sender: TObject);
begin
sPost:='Нет';
Close;
end;
procedure TfrmPost.cmdYesClick(Sender: TObject);
begin
sPost:='Да';
Close;
end;
procedure TfrmPost.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=TCloseAction.caFree;
frmPost:=nil;
end;
end.
Если в первом окне была логическая переменная bModal, то в данном текстовая переменная sPost, которая также может быть объявлена в главной форме приложения.
Окно, предупреждающее о том, что производится попытка удаления данных
Ну и последнее окно — окно с запросом на удаление данных, которое мы назовем frmDelete:

Полный под формы данного окна:
unit delete_;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Effects, FMX.Objects;
type
TfrmDelete = class(TForm)
cmdCancel: TButton;
lblMessage: TLabel;
cmdDelete: TButton;
StyleBook1: TStyleBook;
recClient: TRectangle;
ShadowEffect1: TShadowEffect;
procedure cmdCancelClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure cmdDeleteClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmDelete: TfrmDelete;
implementation
{$R *.fmx}
uses mdi;
procedure TfrmDelete.cmdCancelClick(Sender: TObject);
begin
bModal:=false;
Close;
end;
procedure TfrmDelete.cmdDeleteClick(Sender: TObject);
begin
bModal:=true;
Close;
end;
procedure TfrmDelete.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=TCloseAction.caFree;
frmDelete:=nil;
end;
end.
Организация взаимодействия диалоговых окон
Теперь, когда у нас есть готовые модули четырех типов окон, можем рассмотреть принцип использования.
Использовать перечисленные формы очень просто. В нужный момент их надо просто создавать из кода вот и все. Но при каждом создании желательно указывать координаты их появления. Можно конечно их всегда выводить в центре рабочего стола, но это не эстетично.
Более интересно и красиво, когда они выводятся в центре того окна, из которого они вызываются. А еще под них можно сделать затемненную подложку. То есть между формой, из которой вызывается модальное окно и самим модальным окном сделаем затемненную подложку — полупрозрачную форму без рамок. Мы назовем ее frmGray и у нее будет всего одна процедура onFormClose:
unit gray_;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects;
type
TfrmGray = class(TForm)
Rectangle1: TRectangle;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmGray: TfrmGray;
implementation
{$R *.fmx}
procedure TfrmGray.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=TCloseAction.caFree;
frmGray:=nil;
end;
end.
В конструкторе форм мы лишь зададим данной форме полупрозрачный цвет. Это вы можете сделать на свое усмотрение, а потом подрегулировать. Также мы укажем у свойства FormStyle значение Normal и WindowState = wsMaximized. Не забудьте убрать рамку формы.
Затем мы создадим модуль dialogs_:
unit dialogs_;
interface
uses
Windows, Messages, FMX.Forms, SysUtils, gray_, question_, mdi, message_, post_, delete_,
UITypes;
procedure QuestionCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
procedure MessageCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
procedure PostCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
procedure DeleteCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
implementation
procedure QuestionCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
begin
Application.CreateForm(TfrmGray,frmGray);
frmGray.Show;
Application.CreateForm(TfrmQuestion, frmQuestion);
frmQuestion.Left:=Round(Left +(Width-frmQuestion.Width)/2);
frmQuestion.Top:=Round(Top +(Height-frmQuestion.Height)/2+20);
frmQuestion.lblMessage.Text:=Text;
frmQuestion.ShowModal;
frmGray.Close;
end;
procedure MessageCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
begin
Application.CreateForm(TfrmGray,frmGray);
frmGray.Show;
Application.CreateForm(TfrmMessage, frmMessage);
frmMessage.Left:=Round(Left +(Width-frmMessage.Width)/2);
frmMessage.Top:=Round(Top +(Height-frmMessage.Height)/2+20);
frmMessage.lblMessage.Text:=Text;
frmMessage.ShowModal;
frmGray.Close;
end;
procedure PostCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
begin
Application.CreateForm(TfrmGray,frmGray);
frmGray.Show;
Application.CreateForm(TfrmPost, frmPost);
frmPost.Left:=Round(Left +(Width-frmPost.Width)/2);
frmPost.Top:=Round(Top +(Height-frmPost.Height)/2+20);
frmPost.lblMessage.Text:=Text;
frmPost.ShowModal;
frmGray.Close;
end;
procedure DeleteCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
begin
Application.CreateForm(TfrmGray,frmGray);
frmGray.Show;
Application.CreateForm(TfrmDelete, frmDelete);
frmDelete.Left:=Round(Left +(Width-frmDelete.Width)/2);
frmDelete.Top:=Round(Top +(Height-frmDelete.Height)/2+20);
frmDelete.lblMessage.Text:=Text;
frmDelete.ShowModal;
frmGray.Close;
end;
end.
Обратите внимание на процедуры этого модуля:
procedure QuestionCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String); procedure MessageCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String); procedure PostCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String); procedure DeleteCreate(Left: integer; Top: integer; Height: integer; Width: integer; Text: String);
В каждую из этих процедур передаются габариты и координаты формы, из которой вызывается одно из четырех наших модальных окон. Конечный параметр Text является передаваемой текстовой строкой — сообщение, которое будет появляться в центре окна.
Здесь в секции uses обязательно должны быть подключены все наши создаваемые ранее модули.
Теперь посмотрим на несколько примеров того, как мы можем вызывать свои созданные модальные окна вместо системных.
Например, у нас есть форма frmRacks и мы собираемся удалить запись из набора данных fdRacks, расположенного на этой форме. Для этого у набора данных fdRacks в событии onBeforeDelete, возникающее перед непосредственным удалением записи из набора данных, мы напишем следующий обработчик:
procedure TfrmRacks.fdRacksBeforeDelete(DataSet: TDataSet);
begin
DeleteCreate(Left,Top,Height,Width,'Удалить стойку?');
if bModal=false then abort;
end;
Мы вызываем нашу процедуру DeleteCreate и передадим в нее размеры и координаты нашей формы frmRacks, а также сообщение, которое мы хотим видеть в окне.
При этом, если мы вернемся в процедуру DeleteCreate, то увидим, что в ней мы создавали окно frmDelete. И после создания мы написали, что будем показывать его как ShowModal, то есть, как модальное. Это значит, что код программы в одном текущем потоке остановится, пока мы не закром данное окно.
Следовательно из двух строк нашей процедуры procedure onBeforeDelete будет выполнена только первая. Вторая строка,
if bModal=false then abort;
будет выполнена после закрытия модального окна. А мы вспомним, что когда мы закрываем окно, то мы делаем это одной из двух кнопок, либо «Удалить», либо «Отмена». Когда мы нажимаем н а кнопку «Отмена», то глобальная переменная bModal принимает значение false. Если так, то получается пользователь отказался от удаления (передумал), а значит можно написать команду abort и прекратить удаление.
Результат же команды
DeleteCreate(Left,Top,Height,Width,'Удалить стойку?');
будет такой:

Видите, для чего нужно затемнение? Если его убрать, то наше модальное окно сольется основным. Ну и теперь наше окно конечно же не похоже на стандартное окно приложения, а выполнено в единой стилистике.
Остальные перечисленные окна вызываются точно также, как и frmDelete, только нужно будет вызывать соответствующие процедуры из модуля unit dialogs_.




































