How to properly mimic a modal dialog and wait for input? - delphi

In Firemonkey, message dialogs have changed in Delphi 10.1 Berlin, and MessageDlg has been deprecated in favor to use the new dialog services. However, in any case, I would like to bypass any system dialogs (at least for messages) and use my own synchronous in-form dialog instead.
I managed to write a single form to accomplish this, and it works. However, it's extremely sloppy, specifically the method of how it waits. I don't want to use a callback procedure, so I want my own version of MessageDlg to instead wait for a response from the user, just like regular modal dialogs. (Actually, I call mine MsgPrompt.)
In particular, I need to do something else at this spot:
while not F.FDone do begin
Application.ProcessMessages;
Sleep(50);
end;
... for obvious reasons.
One example of why I don't want (and can't use) a callback procedure, is because I need to use it in the main form's OnCloseQuery, and prompt the user if they're sure they want to close. It would be impossible to make that work, because the OnCloseQuery event handler would exit before the user made a choice
How should I appropriately wait for this input synchronously (mimicking a modal dialog) without blocking the main UI thread and hindering its responsiveness?
Custom dialog unit - please refer to where I say HORRIBLE, HORRIBLE DESIGN:
unit uDialog;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
FMX.Controls.Presentation, FMX.Layouts, System.ImageList, FMX.ImgList;
type
TDialogForm = class(TForm)
DialogLayout: TLayout;
DimPanel: TPanel;
DialogPanel: TPanel;
ButtonPanel: TPanel;
btnYes: TButton;
btnNo: TButton;
btnOK: TButton;
btnCancel: TButton;
btnAbort: TButton;
btnRetry: TButton;
btnIgnore: TButton;
btnAll: TButton;
btnNoToAll: TButton;
btnYesToAll: TButton;
btnHelp: TButton;
btnClose: TButton;
DialogLabel: TLabel;
imgError: TImageControl;
imgInfo: TImageControl;
imgConfirm: TImageControl;
imgWarn: TImageControl;
procedure FormCreate(Sender: TObject);
procedure DialogButtonClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FCloseDialogProc: TInputCloseDialogProc;
FDone: Boolean;
procedure ShowButtons(const AButtons: TMsgDlgButtons);
procedure ShowIcon(const ADialogType: TMsgDlgType);
procedure SetDefaultButton(const ABtn: TMsgDlgBtn);
public
end;
var
DialogForm: TDialogForm;
procedure SetDialogDefaultParent(AValue: TFmxObject);
function MsgPrompt(const AMessage: string;
const ADialogType: TMsgDlgType; const AButtons: TMsgDlgButtons;
const ADefaultButton: TMsgDlgBtn): TModalResult;
procedure MessageDlg(const AMessage: string;
const ADialogType: TMsgDlgType; const AButtons: TMsgDlgButtons;
const ADefaultButton: TMsgDlgBtn; const ACloseDialogProc: TInputCloseDialogProc);
implementation
{$R *.fmx}
var
_DefaultParent: TFmxObject;
procedure SetDialogDefaultParent(AValue: TFmxObject);
begin
_DefaultParent:= AValue;
end;
function MsgPrompt(const AMessage: string;
const ADialogType: TMsgDlgType; const AButtons: TMsgDlgButtons;
const ADefaultButton: TMsgDlgBtn): TModalResult;
var
R: TModalResult;
begin
MessageDlg(AMessage,
ADialogType,
AButtons,
ADefaultButton,
procedure(const AResult: TModalResult)
begin
R:= AResult;
end);
Result:= R;
end;
procedure MessageDlg(const AMessage: string;
const ADialogType: TMsgDlgType; const AButtons: TMsgDlgButtons;
const ADefaultButton: TMsgDlgBtn; const ACloseDialogProc: TInputCloseDialogProc);
var
F: TDialogForm;
begin
F:= TDialogForm.Create(nil);
try
//TODO: Move these assignments into the form itself, perhaps its constructor.
F.FCloseDialogProc:= ACloseDialogProc;
F.DialogLabel.Text:= AMessage;
F.ShowButtons(AButtons);
F.ShowIcon(ADialogType);
F.DialogLayout.Parent:= _DefaultParent;
F.SetDefaultButton(ADefaultButton);
//TODO: Use another method!!!!!!!
while not F.FDone do begin // <---- HORRIBLE, HORRIBLE DESIGN.
Application.ProcessMessages;
Sleep(50);
end;
finally
F.Close;
end;
end;
{ TDialogForm }
procedure TDialogForm.FormCreate(Sender: TObject);
begin
DialogLayout.Align:= TAlignLayout.Client;
DimPanel.Align:= TAlignLayout.Client;
DialogLabel.Text:= '';
end;
procedure TDialogForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:= TCloseAction.caFree;
end;
procedure TDialogForm.DialogButtonClick(Sender: TObject);
var
B: TButton;
R: TModalResult;
begin
DialogLayout.Visible:= False;
B:= TButton(Sender);
case B.Tag of
0: R:= mrYes;
1: R:= mrNo;
2: R:= mrOK;
3: R:= mrCancel;
4: R:= mrAbort;
5: R:= mrRetry;
6: R:= mrIgnore;
7: R:= mrAll;
8: R:= mrNoToAll;
9: R:= mrYesToAll;
10: R:= mrHelp;
11: R:= mrClose;
else R:= mrOK;
end;
FCloseDialogProc(R);
FDone:= True;
end;
procedure TDialogForm.ShowIcon(const ADialogType: TMsgDlgType);
begin
case ADialogType of
TMsgDlgType.mtWarning: imgWarn.Visible:= True;
TMsgDlgType.mtError: imgError.Visible:= True;
TMsgDlgType.mtInformation: imgInfo.Visible:= True;
TMsgDlgType.mtConfirmation: imgConfirm.Visible:= True;
TMsgDlgType.mtCustom: ; //TODO
end;
end;
procedure TDialogForm.SetDefaultButton(const ABtn: TMsgDlgBtn);
var
B: TButton;
begin
B:= nil;
case ABtn of
TMsgDlgBtn.mbYes: B:= btnYes;
TMsgDlgBtn.mbNo: B:= btnNo;
TMsgDlgBtn.mbOK: B:= btnOK;
TMsgDlgBtn.mbCancel: B:= btnCancel;
TMsgDlgBtn.mbAbort: B:= btnAbort;
TMsgDlgBtn.mbRetry: B:= btnRetry;
TMsgDlgBtn.mbIgnore: B:= btnIgnore;
TMsgDlgBtn.mbAll: B:= btnAll;
TMsgDlgBtn.mbNoToAll: B:= btnNoToAll;
TMsgDlgBtn.mbYesToAll: B:= btnYesToAll;
TMsgDlgBtn.mbHelp: B:= btnHelp;
TMsgDlgBtn.mbClose: B:= btnClose;
end;
if Assigned(B) then
if B.Visible then
if B.CanFocus then
B.SetFocus;
end;
procedure TDialogForm.ShowButtons(const AButtons: TMsgDlgButtons);
begin
if TMsgDlgBtn.mbYes in AButtons then begin
btnYes.Visible:= True;
end;
if TMsgDlgBtn.mbNo in AButtons then begin
btnNo.Visible:= True;
end;
if TMsgDlgBtn.mbOK in AButtons then begin
btnOK.Visible:= True;
end;
if TMsgDlgBtn.mbCancel in AButtons then begin
btnCancel.Visible:= True;
end;
if TMsgDlgBtn.mbAbort in AButtons then begin
btnAbort.Visible:= True;
end;
if TMsgDlgBtn.mbRetry in AButtons then begin
btnRetry.Visible:= True;
end;
if TMsgDlgBtn.mbIgnore in AButtons then begin
btnIgnore.Visible:= True;
end;
if TMsgDlgBtn.mbAll in AButtons then begin
btnAll.Visible:= True;
end;
if TMsgDlgBtn.mbNoToAll in AButtons then begin
btnNoToAll.Visible:= True;
end;
if TMsgDlgBtn.mbYesToAll in AButtons then begin
btnYesToAll.Visible:= True;
end;
if TMsgDlgBtn.mbHelp in AButtons then begin
btnHelp.Visible:= True;
end;
if TMsgDlgBtn.mbClose in AButtons then begin
btnClose.Visible:= True;
end;
end;
end.
Custom dialog FMX (NOTE: Image data is removed to spare space):
object DialogForm: TDialogForm
Left = 0
Top = 0
Caption = 'Form2'
ClientHeight = 574
ClientWidth = 503
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnCreate = FormCreate
OnClose = FormClose
DesignerMasterStyle = 0
object DialogLayout: TLayout
Align = Top
Size.Width = 503.000000000000000000
Size.Height = 529.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
object DimPanel: TPanel
Align = Top
Opacity = 0.860000014305114800
Size.Width = 503.000000000000000000
Size.Height = 489.000000000000000000
Size.PlatformDefault = False
TabOrder = 1
object DialogPanel: TPanel
Anchors = [akLeft, akTop, akRight, akBottom]
Position.X = 40.000000000000000000
Position.Y = 40.000000000000000000
Size.Width = 425.000000000000000000
Size.Height = 401.000000000000000000
Size.PlatformDefault = False
StyleLookup = 'DialogPanelStyle1'
TabOrder = 0
object ButtonPanel: TPanel
Align = Bottom
Margins.Left = 3.000000000000000000
Margins.Top = 3.000000000000000000
Margins.Right = 3.000000000000000000
Margins.Bottom = 3.000000000000000000
Position.X = 3.000000000000000000
Position.Y = 355.000000000000000000
Size.Width = 419.000000000000000000
Size.Height = 43.000000000000000000
Size.PlatformDefault = False
StyleLookup = 'Panel2Style1'
TabOrder = 0
object btnYes: TButton
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = 62.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
Text = 'Yes'
Visible = False
OnClick = DialogButtonClick
end
object btnNo: TButton
Tag = 1
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = -274.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 1
Text = 'No'
Visible = False
OnClick = DialogButtonClick
end
object btnOK: TButton
Tag = 2
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = 241.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 2
Text = 'OK'
Visible = False
OnClick = DialogButtonClick
end
object btnCancel: TButton
Tag = 3
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = -610.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 3
Text = 'Cancel'
Visible = False
OnClick = DialogButtonClick
end
object btnAbort: TButton
Tag = 4
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = -778.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 4
Text = 'Abort'
Visible = False
OnClick = DialogButtonClick
end
object btnRetry: TButton
Tag = 5
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = 62.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 5
Text = 'Retry'
Visible = False
OnClick = DialogButtonClick
end
object btnIgnore: TButton
Tag = 6
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = 241.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 6
Text = 'Ignore'
Visible = False
OnClick = DialogButtonClick
end
object btnAll: TButton
Tag = 7
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = -694.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 7
Text = 'All'
Visible = False
OnClick = DialogButtonClick
end
object btnNoToAll: TButton
Tag = 8
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = -22.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 8
Text = 'No to All'
Visible = False
OnClick = DialogButtonClick
end
object btnYesToAll: TButton
Tag = 9
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = 241.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 9
Text = 'Yes to All'
Visible = False
OnClick = DialogButtonClick
end
object btnHelp: TButton
Tag = 10
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = -358.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 10
Text = 'Help'
Visible = False
OnClick = DialogButtonClick
end
object btnClose: TButton
Tag = 11
Align = Right
Cursor = crHandPoint
Margins.Left = 2.000000000000000000
Margins.Top = 2.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 2.000000000000000000
Position.X = -526.000000000000000000
Position.Y = 2.000000000000000000
Size.Width = 80.000000000000000000
Size.Height = 47.000000000000000000
Size.PlatformDefault = False
TabOrder = 11
Text = 'Close'
Visible = False
OnClick = DialogButtonClick
end
end
object DialogLabel: TLabel
Align = Client
StyledSettings = [Family, Style, FontColor]
Margins.Left = 5.000000000000000000
Margins.Top = 5.000000000000000000
Margins.Right = 5.000000000000000000
Margins.Bottom = 5.000000000000000000
Size.Width = 415.000000000000000000
Size.Height = 342.000000000000000000
Size.PlatformDefault = False
TextSettings.Font.Size = 18.000000000000000000
TextSettings.HorzAlign = Center
Text = 'DialogLabel'
end
object imgError: TImageControl
Align = Top
Bitmap.PNG = {}
Margins.Left = 5.000000000000000000
Margins.Top = 5.000000000000000000
Margins.Right = 5.000000000000000000
Margins.Bottom = 5.000000000000000000
Size.Width = 303.000000000000000000
Size.Height = 120.000000000000000000
Size.PlatformDefault = False
TabOrder = 4
Visible = False
end
object imgInfo: TImageControl
Align = Top
Bitmap.PNG = {}
Margins.Left = 5.000000000000000000
Margins.Top = 5.000000000000000000
Margins.Right = 5.000000000000000000
Margins.Bottom = 5.000000000000000000
Position.Y = 49.000000000000000000
Size.Width = 303.000000000000000000
Size.Height = 120.000000000000000000
Size.PlatformDefault = False
TabOrder = 3
Visible = False
end
object imgConfirm: TImageControl
Align = Top
Bitmap.PNG = {}
Margins.Left = 5.000000000000000000
Margins.Top = 5.000000000000000000
Margins.Right = 5.000000000000000000
Margins.Bottom = 5.000000000000000000
Position.Y = 98.000000000000000000
Size.Width = 303.000000000000000000
Size.Height = 120.000000000000000000
Size.PlatformDefault = False
TabOrder = 2
Visible = False
end
object imgWarn: TImageControl
Align = Top
Bitmap.PNG = {}
Margins.Left = 5.000000000000000000
Margins.Top = 5.000000000000000000
Margins.Right = 5.000000000000000000
Margins.Bottom = 5.000000000000000000
Position.Y = 147.000000000000000000
Size.Width = 303.000000000000000000
Size.Height = 120.000000000000000000
Size.PlatformDefault = False
TabOrder = 1
Visible = False
end
end
end
end
end
In the main form's OnCreate event handler, to instruct where to embed these dialogs:
SetDialogDefaultParent(Self);
Usage:
case MsgPrompt('This is a sample message.', TMsgDlgType.mtInformation,
[TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbNo) of
mrYes: begin
//
end;
else begin
//
end;
end;

yes of course doing
while not F.FDone do begin // <---- HORRIBLE, HORRIBLE DESIGN.
Application.ProcessMessages;
Sleep(50);
end;
if totally horrible of the horrible
What i do me, in a very very simple way is :
create a transparent overlay (a simple transparent trectangle) that will catch all mouse event. put this trectangle on the top of your form so all input will be deactivated for mouse event, then construct on the top of this overlay your dialog. in this way the dialog behave like blocking your app. Off course you need to code like javascript and pass to the dialog a reference to a procedure to call on completion and will continue to execute the code
{**************************************************************}
procedure TMyApp_MainForm.ShowPopupDialog(const aTitle: String;
const aSubTitle: String;
const aBody: Tcontrol;
const aButtons: TMsgDlgButtons;
const aDialogCloseProc: TMyApp_PopupDialogCloseProc;
const aAffineRatio: Single = 1);
var aLabel: TALText;
aRectangle: TALRectangle;
aMainPanel: TALrectangle;
aTitleHeight: Single;
aButtonsHeight: Single;
aButton: TMsgDlgBtn;
begin
//free previously created popup (in case)
PopupDialogCloseClick(nil);
//--create the fPopupDialog rect
fPopupDialog := TALRectangle.Create(self);
fPopupDialog.Parent := self;
fPopupDialog.BeginUpdate;
try
//init fPopupDialog
fPopupDialog.Position.Point := TpointF.Create(0,0);
fPopupDialog.Size.Size := TpointF.Create(MyApp_mainForm.clientWidth, MyApp_mainForm.ClientHeight);
fPopupDialog.Anchors := [TAnchorKind.akLeft, TAnchorKind.akTop, TAnchorKind.akRight, TAnchorKind.akBottom];
TALRectangle(fPopupDialog).Fill.Color := $64000000;
TALRectangle(fPopupDialog).Stroke.Kind := TbrushKind.none;
fPopupDialog.OnClick := PopupDialogCloseClick;
//--create the background
aMainPanel := TALRectangle.Create(fPopupDialog);
aMainPanel.Parent := fPopupDialog;
aMainPanel.Fill.Color := $ffffffff;
aMainPanel.Stroke.Kind := TbrushKind.none;
aMainPanel.width := aBody.width; // abody.width must have been correctly setuped
//--create the title
if aTitle <> '' then begin
aLabel := TALText.Create(aMainPanel);
aLabel.Parent := aMainPanel;
aLabel.TextSettings.Font.Style := [TFontStyle.fsBold];
aLabel.TextSettings.Font.Family := MyApp_GetFontFamily('sans-serif', aLabel.TextSettings.Font.Style);
aLabel.TextSettings.Font.size := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TextSettings.FontColor := $FF333844;
aLabel.Height := ALAlignDimensionToPixelRound(50 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TextSettings.VertAlign := TTextAlign.Trailing;
aLabel.Margins.Left := ALAlignDimensionToPixelRound(24 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.Margins.right := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
if aSubTitle = '' then aLabel.Margins.bottom := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale)
else aLabel.Margins.bottom := ALAlignDimensionToPixelRound(3 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TextIsHtml := True;
aLabel.Text := aTitle;
aLabel.Position.Y := 0;
aLabel.Align := TalignLayout.Top;
aTitleHeight := aLabel.Height + aLabel.Margins.top + aLabel.Margins.bottom;
if aSubTitle <> '' then begin
aLabel := TALText.Create(aMainPanel);
aLabel.Parent := aMainPanel;
aLabel.TextSettings.Font.Style := [];
aLabel.TextSettings.Font.Family := MyApp_GetFontFamily('sans-serif-light', aLabel.TextSettings.Font.Style);
aLabel.TextSettings.Font.size := ALAlignDimensionToPixelRound(17 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TextSettings.FontColor := $FF333844;
aLabel.Height := ALAlignDimensionToPixelRound(25 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TextSettings.VertAlign := TTextAlign.Leading;
aLabel.Margins.Left := ALAlignDimensionToPixelRound(24 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.Margins.right := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.Margins.bottom := ALAlignDimensionToPixelRound(12 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TextIsHtml := True;
aLabel.Text := aSubTitle;
aLabel.Position.Y := aTitleHeight + 1;
aLabel.Align := TalignLayout.Top;
aTitleHeight := aTitleHeight + aLabel.Height + aLabel.Margins.top + aLabel.Margins.bottom;
end;
end
else aTitleHeight := 0;
//--create the content
if assigned(aBody.Owner) then aBody.Owner.RemoveComponent(aBody);
aMainPanel.InsertComponent(aBody);
aBody.Parent := aMainPanel;
aBody.Position.Y := aTitleHeight + 1;
aBody.Align := TALignLayout.top;
//--create the buttons
if aButtons <> [] then begin
aRectangle := TALRectangle.Create(aMainPanel);
aRectangle.Parent := aMainPanel;
aRectangle.width := aBody.width;
aRectangle.Padding.Right := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aButtonsHeight := ALAlignDimensionToPixelRound(60 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aRectangle.Height := aButtonsHeight;
arectangle.Fill.color := $fffafafa;
aRectangle.Sides := [TSide.Top];
aRectangle.Stroke.Color := $FFE9E9E9;
for aButton in aButtons do begin
aLabel := TALText.Create(aRectangle);
aLabel.Parent := aRectangle;
aLabel.TextSettings.Font.Style := [];
aLabel.TextSettings.Font.Family := MyApp_GetFontFamily('sans-serif', aLabel.TextSettings.Font.Style);
aLabel.TextSettings.Font.size := ALAlignDimensionToPixelRound(17 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TextSettings.FontColor := $FF398dac;
aLabel.AutoSize := true;
aLabel.Margins.Left := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.Margins.right := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TouchTargetExpansion.Left := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
aLabel.TouchTargetExpansion.right := ALAlignDimensionToPixelRound(20 * aAffineRatio * fAffineDimensionRatio, ScreenScale);
Alabel.HitTest := true;
aLabel.Cursor := CrHandPoint;
aLabel.OnMouseDown := TMyApp_ProcOfObjectWrapper.OnTouchEffect1MouseDownMaxViaTagFloat;
if aButton = TMsgDlgBtn.mbCancel then begin
aLabel.Text := UpperCase(MyApp_translate('_Cancel'));
aLabel.Tag := mrCancel;
aLabel.Position.x := 0;
end
else if aButton = TMsgDlgBtn.mbYes then begin
aLabel.Text := UpperCase(MyApp_translate('_Yes'));
aLabel.Tag := mrYes;
aLabel.Position.x := aRectangle.Width;
end
else if aButton = TMsgDlgBtn.mbOk then begin
aLabel.Text := UpperCase(MyApp_translate('_OK'));
aLabel.Tag := mrOK;
aLabel.Position.x := aRectangle.Width;
end;
aLabel.TagFloat := aButtonsHeight;
aLabel.onclick := PopupDialogBtnClick;
aLabel.Align := TalignLayout.right;
end;
aRectangle.Position.Y := aTitleHeight + aBody.height + 1;
aRectangle.Align := TALignLayout.top;
end
else aButtonsHeight := 0;
finally
ALLockTexts(fPopupDialog);
try
fPopupDialog.EndUpdate;
finally
ALUnLockTexts(fPopupDialog);
end;
end;
//create the bufbitmap
ALFmxMakeBufBitmaps(aMainPanel); // << this not really for the text that already made their bufbitmap in ALUnLockTexts for for images
if aTitleHeight + aButtonsHeight + aBody.Height + aBody.margins.top + aBody.margins.bottom > (Clientheight / 100) * 94 then aBody.Height := ((Clientheight / 100) * 94) - aTitleHeight - aButtonsHeight - aBody.margins.top - aBody.margins.bottom;
aMainPanel.height := aTitleHeight + aButtonsHeight + aBody.Height + aBody.margins.top + aBody.margins.bottom; // << because aBody.Height was probably updated in ALUnLockTexts(fPopupDialog);
aMainPanel.Align := TalignLayout.center;
//--create the shadow effect
aMainPanel.shadow.enabled := true;
aMainPanel.shadow.Shadowcolor := $3C000000;
aMainPanel.shadow.blur := 8 * affinedimensionRatio;
//show the popup
fPopupDialogCloseProc := ADialogCloseProc;
fPopupDialog.Visible := True;
fPopupDialog.BringToFront;
//close popup loading (if any)
closePopupLoading
end;

Your problem is that you want to use it in the CloseQuery event. You can set CanClose:=false then it will fall through and you can use any kind of normal dialog box.
This works on Android. If user clicks off of Dialog box so it disappears, it defaults to No
Uses FMX.DialogService.Async;
procedure TForm2.Button1Click(Sender: TObject);
begin
close;
end;
procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if not GlobalQuit then
begin
CanClose:=false;
TDialogServiceAsync.MessageDialog(
'Quit?',
TMsgDlgType.mtConfirmation,
[TMsgDlgBtn.mbYes,TMsgDlgBtn.mbNo],
TMsgDlgBtn.mbNo,
0,
procedure(const AResult:TModalResult)
begin
if AResult = mrYes then
begin
GlobalQuit := true;
Close; // will go to CloseQuery again
end
else
GlobalQuit := false;
end
);
end;
end;

Related

TTreeViewItem in-place editor using styles

Using Delphi 10.4.2, I am adding an in-place editor for the text property of a custom TreeViewItem of a TTreeView in FMX, similar to the way the VCL version works. After failing to accomplish this in simple code, I decided to try my hand at accomplishing it using FMX Styles. Below is the code for my test project, along with the text of the StyleBook.
This is my first attempt at working with styles and it appears to work the way I expected it to in Windows. But I was wondering if this is the correct way to use styles, or am I missing something and/or using the styles feature wrong in some way?
Added...Or maybe a better question would be Is this the best way to access the "editor" object I added to the default TTreeViewItem style?
Note: For the style, I used the "Edit Custom Style" option for a TTreeViewItems, and simply added a TEdit to it and made it invisible by default. When you double click a node, the logic makes the TEdit visible and sets it text property to that of the Node. When you change the TEdit text and press Return, the TEdit is set to invisible again and the TEdit text is copied to the node text property. I plan on using this same component for Android and Apple versions of my program, but have not tested or worked on that yet. My assumption is that I will need to add the platforms to the StyleBook for the TreeViewItem, but the basic logic should work the same as Windows.
unit Unit1;
interface
uses System.SysUtils, System.Classes, System.UITypes, FMX.Forms, FMX.TreeView, FMX.Controls, FMX.Edit, FMX.Types,
FMX.Layouts;
type
TForm1 = class(TForm)
TreeView1: TTreeView;
StyleBook1: TStyleBook;
procedure FormShow(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
type
TVLinkTreeViewItem = class(TTreeViewItem)
private
fData: string;
procedure TreeViewItem1DblClick(Sender: TObject);
procedure EditorExit(Sender: TObject);
procedure EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
procedure ApplyStyle; override;
public
property Data: string read fData write fData;
constructor Create(AOwner: TComponent); override;
end;
procedure TForm1.FormShow(Sender: TObject);
var
I, c: Integer;
vNode1,
vNode2: TVLinkTreeViewItem;
begin
TreeView1.BeginUpdate;
TreeView1.Clear;
for I := 0 to 4 do
begin
vNode1 := TVLinkTreeViewItem.Create(TreeView1);
vNode1.Text := 'Level 1 - '+ IntToStr(I);
vNode1.Data := vNode1.Name;
TreeView1.AddObject(vNode1);
for c := 0 to 4 do
begin
vNode2 := TVLinkTreeViewItem.Create(vNode1);
vNode2.Text := 'Level 2 - '+ IntToStr(c);
vNode2.Data := vNode2.Name;
vNode1.AddObject(vNode2);
end;
end;
TreeView1.ExpandAll;
treeView1.EndUpdate;
end;
{ TVLinkTreeViewItem }
constructor TVLinkTreeViewItem.Create(AOwner: TComponent);
begin
inherited;
self.StyleLookup := 'VLinkTreeViewItemStyle';
self.ApplyStyleLookup;
fData := '';
end;
procedure TVLinkTreeViewItem.ApplyStyle;
var
vEditor: TEdit;
begin
inherited;
//need to get Editor each time needed. Not sure why,
//but doesn't work when trying to set a component variable called fEditor here,
//then have the OnXXXX routines use fEditor, instead of finding the editor themselves
vEditor:= FindStyleResource('Editor') as TEdit;
if Assigned(vEditor) then
begin
vEditor.OnKeyDown := EditorKeyUp;
OnDblClick := TreeViewItem1DblClick;
vEditor.OnExit := EditorExit;
end;
end;
procedure TVLinkTreeViewItem.TreeViewItem1DblClick(Sender: TObject);
var
vEditor: TEdit;
begin
//need to get Editor each time needed. Not sure why,
//but doesn't work when trying to set a component variable called fEditor in ApplyStyle.
vEditor:= FindStyleResource('Editor') as TEdit;
if assigned(vEditor) then
begin
vEditor.Visible := true;
vEditor.Text := Text;
vEditor.SetFocus;
vEditor.SelectAll;
end;
end;
procedure TVLinkTreeViewItem.EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
var
vEditor: TEdit;
begin
inherited;
//need to get Editor each time needed. Not sure why,
//but doesn't work when trying to set a component variable called fEditor in ApplyStyle.
vEditor:= FindStyleResource('Editor') as TEdit;
if not assigned(vEditor) then
exit;
if Key = vkReturn then
begin
Text := vEditor.Text;
vEditor.Visible := false;
end
else if Key in [vkEscape, vkCancel, vkTab, vkHardwareBack] then
begin
vEditor.Visible := false;
end;
end;
procedure TVLinkTreeViewItem.EditorExit(Sender: TObject);
var
vEditor: TEdit;
begin
//need to get Editor each time needed. Not sure why,
//but doesn't work when trying to set a component variable called fEditor in ApplyStyle.
vEditor:= FindStyleResource('Editor') as TEdit;
if Assigned(vEditor) then
vEditor.Visible := false;
end;
end.
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 500
ClientWidth = 640
StyleBook = StyleBook1
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnShow = FormShow
DesignerMasterStyle = 0
object TreeView1: TTreeView
Align = Left
Size.Width = 269.000000000000000000
Size.Height = 500.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
Viewport.Width = 265.000000000000000000
Viewport.Height = 496.000000000000000000
end
object StyleBook1: TStyleBook
Styles = <
item
end
item
Platform = 'Windows 10 Desktop'
ResourcesBin = {
464D585F5354594C4520322E35010616564C696E6B5472656556696577497465
6D5374796C650395050631564C696E6B54726565566965774974656D5374796C
657472656576696577657870616E646572627574746F6E7374796C6503E10306
23564C696E6B54726565566965774974656D5374796C65436865636B426F7853
74796C6503E40B005450463007544C61796F757400095374796C654E616D6506
16564C696E6B54726565566965774974656D5374796C6505416C69676E070643
656E7465720A53697A652E576964746805000000000000808407400B53697A65
2E4865696768740500000000000000A003401453697A652E506C6174666F726D
44656661756C7408085461624F726465720236000C545370656564427574746F
6E00095374796C654E616D650606627574746F6E05416C69676E07044C656674
0C4D617267696E732E4C6566740500000000000000C000400B4D617267696E73
2E546F700500000000000000C000400D4D617267696E732E5269676874050000
0000000000C000400E4D617267696E732E426F74746F6D0500000000000000C0
00400A506F736974696F6E2E580500000000000000C000400A506F736974696F
6E2E590500000000000000C000400A53697A652E576964746805000000000000
00F002400B53697A652E4865696768740500000000000000E002401453697A65
2E506C6174666F726D44656661756C74080B5374796C654C6F6F6B7570063156
4C696E6B54726565566965774974656D5374796C657472656576696577657870
616E646572627574746F6E7374796C65000007544C61796F75740005416C6967
6E0708436F6E74656E74730C4D617267696E732E4C6566740500000000000000
A003400A53697A652E57696474680500000000000000F506400B53697A652E48
65696768740500000000000000A003401453697A652E506C6174666F726D4465
6661756C7408000954436865636B426F7800095374796C654E616D6506056368
65636B05416C69676E07044C6566740843616E466F637573081244697361626C
65466F637573456666656374090A53697A652E57696474680500000000000000
A003400B53697A652E4865696768740500000000000000A003401453697A652E
506C6174666F726D44656661756C74080B5374796C654C6F6F6B75700623564C
696E6B54726565566965774974656D5374796C65436865636B426F785374796C
6500000654476C79706800095374796C654E616D65060A676C7970687374796C
650C4D617267696E732E4C65667405000000000000008000400B4D617267696E
732E546F70050000000000000080FF3F0D4D617267696E732E52696768740500
0000000000008000400E4D617267696E732E426F74746F6D0500000000000000
80FF3F05416C69676E07044C6566740A53697A652E5769647468050000000000
00008003400B53697A652E48656967687405000000000000008003401453697A
652E506C6174666F726D44656661756C7408000016544163746976655374796C
65546578744F626A65637400095374796C654E616D6506047465787405416C69
676E0706436C69656E74064C6F636B6564090A53697A652E5769647468050000
0000000000E106400B53697A652E4865696768740500000000000000A0034014
53697A652E506C6174666F726D44656661756C74080454657874060454657874
155465787453657474696E67732E576F72645772617008165465787453657474
696E67732E486F727A416C69676E07074C656164696E670D536861646F775669
7369626C65080D41637469766554726967676572070853656C65637465640B41
6374697665436F6C6F720708636C61426C61636B000005544564697400095374
796C654E616D650606456469746F7219546F7563682E496E7465726163746976
6547657374757265730B074C6F6E6754617009446F75626C655461700005416C
69676E0706436C69656E740B5374796C654C6F6F6B7570060965646974737479
6C65085461624F7264657202000A53697A652E57696474680500000000000000
E106400B53697A652E4865696768740500000000000000A003401453697A652E
506C6174666F726D44656661756C74080756697369626C650800000000545046
3007544C61796F757400095374796C654E616D650631564C696E6B5472656556
6965774974656D5374796C657472656576696577657870616E64657262757474
6F6E7374796C6505416C69676E070643656E7465720B4D617267696E732E546F
70050000000000000080FF3F0A53697A652E5769647468050000000000008084
07400B53697A652E4865696768740500000000000000A003401453697A652E50
6C6174666F726D44656661756C74080756697369626C6508085461624F726465
720237000554506174680005416C69676E070643656E74657209446174612E50
6174680A400000000500000000000000D36D3F431BEF4843010000001749E043
BA09E54301000000D36D3F43C73B344401000000D36D3F431BEF484303000000
D36D3F431BEF48430A46696C6C2E436F6C6F720708636C61426C61636B064C6F
636B6564090748697454657374080A53697A652E576964746805000000000000
00E001400B53697A652E4865696768740500000000000000E001401453697A65
2E506C6174666F726D44656661756C74080B5374726F6B652E4B696E6407044E
6F6E65000F54466C6F6174416E696D6174696F6E00084475726174696F6E0500
0000000017B7D1F13F0C50726F70657274794E616D6506074F7061636974790A
537461727456616C756505000000000000000000000953746F7056616C756505
0000000000000080FF3F075472696767657206104973457870616E6465643D66
616C73650E54726967676572496E7665727365060F4973457870616E6465643D
747275650000000554506174680005416C69676E070643656E74657209446174
612E506174680A400000000500000000000000CB11CF4379E93C4301000000CB
11CF4396230A44010000007DBF1E4296230A4401000000CB11CF4379E93C4303
000000CB11CF4379E93C430A46696C6C2E436F6C6F720708636C61426C61636B
064C6F636B656409074869745465737408074F70616369747905000000000000
000000000A53697A652E57696474680500000000000000E001400B53697A652E
4865696768740500000000000000E001401453697A652E506C6174666F726D44
656661756C74080B5374726F6B652E4B696E6407044E6F6E65000F54466C6F61
74416E696D6174696F6E00084475726174696F6E05000000000017B7D1F13F0C
50726F70657274794E616D6506074F7061636974790A537461727456616C7565
05000000000000000000000953746F7056616C7565050000000000000080FF3F
0754726967676572060F4973457870616E6465643D747275650E547269676765
72496E766572736506104973457870616E6465643D66616C7365000000005450
463007544C61796F757400095374796C654E616D650623564C696E6B54726565
566965774974656D5374796C65436865636B426F785374796C6505416C69676E
070643656E7465720A53697A652E576964746805000000000000808407400B53
697A652E4865696768740500000000000000A003401453697A652E506C617466
6F726D44656661756C74080756697369626C6508085461624F72646572021200
07544C61796F75740005416C69676E07044C6566740A53697A652E5769647468
05000000000000009003400B53697A652E4865696768740500000000000000A0
03401453697A652E506C6174666F726D44656661756C7408001154436865636B
5374796C654F626A65637400095374796C654E616D65060A6261636B67726F75
6E6405416C69676E070643656E746572074361704D6F6465070454696C65064C
6F636B6564090C536F757263654C6F6F6B7570061B57696E646F777320313020
4465736B746F707374796C652E706E670A53697A652E57696474680500000000
000000D002400B53697A652E4865696768740500000000000000D00240145369
7A652E506C6174666F726D44656661756C740808577261704D6F646507064365
6E7465720D416374697665547269676765720707436865636B65640A41637469
76654C696E6B0E010F536F75726365526563742E4C6566740500000000000000
F803400E536F75726365526563742E546F70050000000000000092064010536F
75726365526563742E52696768740500000000000000B0044011536F75726365
526563742E426F74746F6D05000000000000009F06400001055363616C650500
000000000000C0FF3F0F536F75726365526563742E4C65667405000000000000
00B804400E536F75726365526563742E546F700500000000000000DB06401053
6F75726365526563742E5269676874050000000000000082054011536F757263
65526563742E426F74746F6D0500000000000000EE06400001055363616C6505
000000000000008000400F536F75726365526563742E4C656674050000000000
0000F804400E536F75726365526563742E546F70050000000000000092074010
536F75726365526563742E52696768740500000000000000B0054011536F7572
6365526563742E426F74746F6D05000000000000009F074000000A536F757263
654C696E6B0E010F536F75726365526563742E4C6566740500000000000000C0
00400E536F75726365526563742E546F70050000000000000092064010536F75
726365526563742E5269676874050000000000000080034011536F7572636552
6563742E426F74746F6D05000000000000009F06400001055363616C65050000
0000000000C0FF3F0F536F75726365526563742E4C6566740500000000000000
8001400E536F75726365526563742E546F700500000000000000DB064010536F
75726365526563742E52696768740500000000000000B8034011536F75726365
526563742E426F74746F6D0500000000000000EE06400001055363616C650500
0000000000008000400F536F75726365526563742E4C65667405000000000000
00C001400E536F75726365526563742E546F7005000000000000009207401053
6F75726365526563742E5269676874050000000000000080044011536F757263
65526563742E426F74746F6D05000000000000009F0740000007486F744C696E
6B0E010F536F75726365526563742E4C65667405000000000000008803400E53
6F75726365526563742E546F70050000000000000092064010536F7572636552
6563742E52696768740500000000000000F0034011536F75726365526563742E
426F74746F6D05000000000000009F06400001055363616C6505000000000000
00C0FF3F0F536F75726365526563742E4C6566740500000000000000C803400E
536F75726365526563742E546F700500000000000000DB064010536F75726365
526563742E52696768740500000000000000B0044011536F7572636552656374
2E426F74746F6D0500000000000000EE06400001055363616C65050000000000
00008000400F536F75726365526563742E4C6566740500000000000000880440
0E536F75726365526563742E546F70050000000000000092074010536F757263
65526563742E52696768740500000000000000F0044011536F75726365526563
742E426F74746F6D05000000000000009F074000000D416374697665486F744C
696E6B0E010F536F75726365526563742E4C6566740500000000000000B40440
0E536F75726365526563742E546F70050000000000000092064010536F757263
65526563742E52696768740500000000000000E8044011536F75726365526563
742E426F74746F6D05000000000000009F06400001055363616C650500000000
000000C0FF3F0F536F75726365526563742E4C65667405000000000000008605
400E536F75726365526563742E546F700500000000000000DB064010536F7572
6365526563742E52696768740500000000000000AC054011536F757263655265
63742E426F74746F6D0500000000000000EE06400001055363616C6505000000
000000008000400F536F75726365526563742E4C6566740500000000000000B4
05400E536F75726365526563742E546F70050000000000000092074010536F75
726365526563742E52696768740500000000000000E8054011536F7572636552
6563742E426F74746F6D05000000000000009F074000000B466F63757365644C
696E6B0E010F536F75726365526563742E4C6566740500000000000000880340
0E536F75726365526563742E546F70050000000000000092064010536F757263
65526563742E52696768740500000000000000F0034011536F75726365526563
742E426F74746F6D05000000000000009F06400001055363616C650500000000
000000C0FF3F0F536F75726365526563742E4C6566740500000000000000C803
400E536F75726365526563742E546F700500000000000000DB064010536F7572
6365526563742E52696768740500000000000000B0044011536F757263655265
63742E426F74746F6D0500000000000000EE06400001055363616C6505000000
000000008000400F536F75726365526563742E4C656674050000000000000088
04400E536F75726365526563742E546F70050000000000000092074010536F75
726365526563742E52696768740500000000000000F0044011536F7572636552
6563742E426F74746F6D05000000000000009F0740000011416374697665466F
63757365644C696E6B0E010F536F75726365526563742E4C6566740500000000
000000B404400E536F75726365526563742E546F700500000000000000920640
10536F75726365526563742E52696768740500000000000000E8044011536F75
726365526563742E426F74746F6D05000000000000009F06400001055363616C
650500000000000000C0FF3F0F536F75726365526563742E4C65667405000000
000000008605400E536F75726365526563742E546F700500000000000000DB06
4010536F75726365526563742E52696768740500000000000000AC054011536F
75726365526563742E426F74746F6D0500000000000000EE0640000105536361
6C6505000000000000008000400F536F75726365526563742E4C656674050000
0000000000B405400E536F75726365526563742E546F70050000000000000092
074010536F75726365526563742E52696768740500000000000000E805401153
6F75726365526563742E426F74746F6D05000000000000009F07400000000000
1654427574746F6E5374796C65546578744F626A65637400095374796C654E61
6D6506047465787405416C69676E0706436C69656E74064C6F636B6564090C4D
617267696E732E4C6566740500000000000000C000400A53697A652E57696474
680500000000000000F406400B53697A652E4865696768740500000000000000
A003401453697A652E506C6174666F726D44656661756C74080D536861646F77
56697369626C650808486F74436F6C6F720708636C61426C61636B0C466F6375
736564436F6C6F720708636C61426C61636B0B4E6F726D616C436F6C6F720708
636C61426C61636B0C50726573736564436F6C6F720708636C61426C61636B00
0000}
end>
Left = 116
Top = 150
end
end
object TStyleContainer
object TLayout
StyleName = 'VLinkTreeViewItemStyle'
Align = Center
Size.Width = 265.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
Visible = False
TabOrder = 52
object TSpeedButton
StyleName = 'button'
Align = Left
Margins.Left = 3.000000000000000000
Margins.Top = 3.000000000000000000
Margins.Right = 3.000000000000000000
Margins.Bottom = 3.000000000000000000
Position.X = 3.000000000000000000
Position.Y = 3.000000000000000000
Size.Width = 15.000000000000000000
Size.Height = 14.000000000000000000
Size.PlatformDefault = False
StyleLookup = 'VLinkTreeViewItemStyletreeviewexpanderbuttonstyle'
end
object TLayout
Align = Contents
Margins.Left = 20.000000000000000000
Size.Width = 245.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
object TCheckBox
StyleName = 'check'
Align = Left
CanFocus = False
DisableFocusEffect = True
Size.Width = 20.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
StyleLookup = 'VLinkTreeViewItemStyleCheckBoxStyle'
end
object TGlyph
StyleName = 'glyphstyle'
Margins.Left = 2.000000000000000000
Margins.Top = 1.000000000000000000
Margins.Right = 2.000000000000000000
Margins.Bottom = 1.000000000000000000
Align = Left
Size.Width = 16.000000000000000000
Size.Height = 16.000000000000000000
Size.PlatformDefault = False
end
object TActiveStyleTextObject
StyleName = 'text'
Align = Client
Locked = True
Size.Width = 225.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
Text = 'Text'
TextSettings.WordWrap = False
TextSettings.HorzAlign = Leading
ShadowVisible = False
ActiveTrigger = Selected
ActiveColor = claBlack
end
object TEdit
StyleName = 'Editor'
Touch.InteractiveGestures = [LongTap, DoubleTap]
Align = Client
StyleLookup = 'editstyle'
TabOrder = 0
Size.Width = 225.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
Visible = False
end
end
end
object TLayout
StyleName = 'VLinkTreeViewItemStyletreeviewexpanderbuttonstyle'
Align = Center
Margins.Top = 1.000000000000000000
Size.Width = 265.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
Visible = False
TabOrder = 54
object TPath
Align = Center
Data.Path = {
0500000000000000D36D3F431BEF4843010000001749E043BA09E54301000000
D36D3F43C73B344401000000D36D3F431BEF484303000000D36D3F431BEF4843}
Fill.Color = claBlack
Locked = True
HitTest = False
Size.Width = 7.000000000000000000
Size.Height = 7.000000000000000000
Size.PlatformDefault = False
Stroke.Kind = None
object TFloatAnimation
Duration = 0.000099999997473788
PropertyName = 'Opacity'
StartValue = 0.000000000000000000
StopValue = 1.000000000000000000
Trigger = 'IsExpanded=false'
TriggerInverse = 'IsExpanded=true'
end
end
object TPath
Align = Center
Data.Path = {
0500000000000000CB11CF4379E93C4301000000CB11CF4396230A4401000000
7DBF1E4296230A4401000000CB11CF4379E93C4303000000CB11CF4379E93C43}
Fill.Color = claBlack
Locked = True
HitTest = False
Opacity = 0.000000000000000000
Size.Width = 7.000000000000000000
Size.Height = 7.000000000000000000
Size.PlatformDefault = False
Stroke.Kind = None
object TFloatAnimation
Duration = 0.000099999997473788
PropertyName = 'Opacity'
StartValue = 0.000000000000000000
StopValue = 1.000000000000000000
Trigger = 'IsExpanded=true'
TriggerInverse = 'IsExpanded=false'
end
end
end
object TLayout
StyleName = 'VLinkTreeViewItemStyleCheckBoxStyle'
Align = Center
Size.Width = 265.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
Visible = False
TabOrder = 18
object TLayout
Align = Left
Size.Width = 18.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
object TCheckStyleObject
StyleName = 'background'
Align = Center
CapMode = Tile
Locked = True
SourceLookup = 'Windows 10 Desktopstyle.png'
Size.Width = 13.000000000000000000
Size.Height = 13.000000000000000000
Size.PlatformDefault = False
WrapMode = Center
ActiveTrigger = Checked
ActiveLink = <
item
SourceRect.Left = 31.000000000000000000
SourceRect.Top = 146.000000000000000000
SourceRect.Right = 44.000000000000000000
SourceRect.Bottom = 159.000000000000000000
end
item
Scale = 1.500000000000000000
SourceRect.Left = 46.000000000000000000
SourceRect.Top = 219.000000000000000000
SourceRect.Right = 65.000000000000000000
SourceRect.Bottom = 238.000000000000000000
end
item
Scale = 2.000000000000000000
SourceRect.Left = 62.000000000000000000
SourceRect.Top = 292.000000000000000000
SourceRect.Right = 88.000000000000000000
SourceRect.Bottom = 318.000000000000000000
end>
SourceLink = <
item
SourceRect.Left = 3.000000000000000000
SourceRect.Top = 146.000000000000000000
SourceRect.Right = 16.000000000000000000
SourceRect.Bottom = 159.000000000000000000
end
item
Scale = 1.500000000000000000
SourceRect.Left = 4.000000000000000000
SourceRect.Top = 219.000000000000000000
SourceRect.Right = 23.000000000000000000
SourceRect.Bottom = 238.000000000000000000
end
item
Scale = 2.000000000000000000
SourceRect.Left = 6.000000000000000000
SourceRect.Top = 292.000000000000000000
SourceRect.Right = 32.000000000000000000
SourceRect.Bottom = 318.000000000000000000
end>
HotLink = <
item
SourceRect.Left = 17.000000000000000000
SourceRect.Top = 146.000000000000000000
SourceRect.Right = 30.000000000000000000
SourceRect.Bottom = 159.000000000000000000
end
item
Scale = 1.500000000000000000
SourceRect.Left = 25.000000000000000000
SourceRect.Top = 219.000000000000000000
SourceRect.Right = 44.000000000000000000
SourceRect.Bottom = 238.000000000000000000
end
item
Scale = 2.000000000000000000
SourceRect.Left = 34.000000000000000000
SourceRect.Top = 292.000000000000000000
SourceRect.Right = 60.000000000000000000
SourceRect.Bottom = 318.000000000000000000
end>
ActiveHotLink = <
item
SourceRect.Left = 45.000000000000000000
SourceRect.Top = 146.000000000000000000
SourceRect.Right = 58.000000000000000000
SourceRect.Bottom = 159.000000000000000000
end
item
Scale = 1.500000000000000000
SourceRect.Left = 67.000000000000000000
SourceRect.Top = 219.000000000000000000
SourceRect.Right = 86.000000000000000000
SourceRect.Bottom = 238.000000000000000000
end
item
Scale = 2.000000000000000000
SourceRect.Left = 90.000000000000000000
SourceRect.Top = 292.000000000000000000
SourceRect.Right = 116.000000000000000000
SourceRect.Bottom = 318.000000000000000000
end>
FocusedLink = <
item
SourceRect.Left = 17.000000000000000000
SourceRect.Top = 146.000000000000000000
SourceRect.Right = 30.000000000000000000
SourceRect.Bottom = 159.000000000000000000
end
item
Scale = 1.500000000000000000
SourceRect.Left = 25.000000000000000000
SourceRect.Top = 219.000000000000000000
SourceRect.Right = 44.000000000000000000
SourceRect.Bottom = 238.000000000000000000
end
item
Scale = 2.000000000000000000
SourceRect.Left = 34.000000000000000000
SourceRect.Top = 292.000000000000000000
SourceRect.Right = 60.000000000000000000
SourceRect.Bottom = 318.000000000000000000
end>
ActiveFocusedLink = <
item
SourceRect.Left = 45.000000000000000000
SourceRect.Top = 146.000000000000000000
SourceRect.Right = 58.000000000000000000
SourceRect.Bottom = 159.000000000000000000
end
item
Scale = 1.500000000000000000
SourceRect.Left = 67.000000000000000000
SourceRect.Top = 219.000000000000000000
SourceRect.Right = 86.000000000000000000
SourceRect.Bottom = 238.000000000000000000
end
item
Scale = 2.000000000000000000
SourceRect.Left = 90.000000000000000000
SourceRect.Top = 292.000000000000000000
SourceRect.Right = 116.000000000000000000
SourceRect.Bottom = 318.000000000000000000
end>
end
end
object TButtonStyleTextObject
StyleName = 'text'
Align = Client
Locked = True
Margins.Left = 3.000000000000000000
Size.Width = 244.000000000000000000
Size.Height = 20.000000000000000000
Size.PlatformDefault = False
ShadowVisible = False
HotColor = claBlack
FocusedColor = claBlack
NormalColor = claBlack
PressedColor = claBlack
end
end
end

FMX Custom Header for TStringGrid

I am using this code to set the column Headers for my TStringGrid (FMX - 10.4.1)
procedure TForm1.StringGrid1ApplyStyleLookup(Sender: TObject);
var
Header: THeader;
HeaderItem: THeaderItem;
I: Integer;
begin
Header:= THeader((Sender as TStringGrid).FindStyleResource('header'));
if Assigned(Header) then
begin
for I := 0 to pred(Header.Count) do
begin
HeaderItem:= Header.Items[I];
HeaderItem.StyledSettings := HeaderItem.StyledSettings - [TStyledSetting.Size, TStyledSetting.FontColor];
HeaderItem.Font.Size := 20;
HeaderItem.FontColor:= TAlphaColors.Blue;
HeaderItem.TextSettings.HorzAlign := TTextAlign.Center;
HeaderItem.TextSettings.VertAlign := TTextAlign.Center;
end;
Header.Height := 28;
end;
end;
I get this result as expected
However, if I'm updating the list with some new data, the header is back to default style
Why is it different now? Why ApplyStyleLookup is applied only once ?
How can I make sure the correct settings will be applied to my headers each and every time ?
Thanks
Here below a sample code
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 400
ClientWidth = 600
Position = DesktopCenter
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
DesignerMasterStyle = 0
object StringGrid1: TStringGrid
Align = Client
CanFocus = True
ClipChildren = True
Margins.Left = 5.000000000000000000
Margins.Top = 50.000000000000000000
Margins.Right = 5.000000000000000000
Margins.Bottom = 5.000000000000000000
Size.Width = 590.000000000000000000
Size.Height = 345.000000000000000000
Size.PlatformDefault = False
StyleLookup = 'gridstyle'
TabOrder = 0
RowCount = 0
Options = [ColumnResize, ColLines, RowLines, RowSelect, Tabs, Header]
OnApplyStyleLookup = StringGrid1ApplyStyleLookup
Viewport.Width = 586.000000000000000000
Viewport.Height = 320.000000000000000000
object StringColumn1: TStringColumn
Header = 'Test'
end
end
object Button1: TButton
Position.X = 8.000000000000000000
Position.Y = 8.000000000000000000
Size.Width = 177.000000000000000000
Size.Height = 33.000000000000000000
Size.PlatformDefault = False
TabOrder = 1
Text = 'Show Form Properties'
OnClick = Button1Click
end
object Text1: TText
Anchors = [akLeft, akTop, akRight]
Position.X = 192.000000000000000000
Position.Y = 8.000000000000000000
Size.Width = 401.000000000000000000
Size.Height = 33.000000000000000000
Size.PlatformDefault = False
Text = 'Unkown'
TextSettings.HorzAlign = Trailing
end
end
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
System.Rtti, System.TypInfo,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Grid.Style,
FMX.StdCtrls, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Grid, FMX.Header,
FMX.Objects;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
Button1: TButton;
Text1: TText;
StringColumn1: TStringColumn;
procedure Button1Click(Sender: TObject);
procedure StringGrid1ApplyStyleLookup(Sender: TObject);
private
{ Private declarations }
FCount: cardinal;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.Button1Click(Sender: TObject);
var
PropList: PPropList;
PropCount, PropIndex: Integer;
begin
StringGrid1.ClearColumns;
PropCount:= GetPropList(Form1, PropList);
StringGrid1.RowCount:= PropCount;
StringGrid1.RowHeight:= 20;
StringGrid1.AddObject(TStringColumn.Create(StringGrid1));
StringGrid1.Columns[0].Width:= (StringGrid1.Width - 24) / 2;
StringGrid1.Columns[0].HorzAlign:= TTextAlign.Leading;
StringGrid1.Columns[0].Header:= 'Property';
StringGrid1.AddObject(TStringColumn.Create(StringGrid1));
StringGrid1.Columns[1].Width:= (StringGrid1.Width - 24) / 2;
StringGrid1.Columns[1].HorzAlign:= TTextAlign.Leading;
StringGrid1.Columns[1].Header:= 'Value';
for PropIndex:= 0 to pred(PropCount) do
begin
StringGrid1.Cells[0, PropIndex]:= PropList[PropIndex].Name;
StringGrid1.Cells[1, PropIndex]:= GetPropValue(Form1, PropList[PropIndex].Name, true);
end;
end;
procedure TForm1.StringGrid1ApplyStyleLookup(Sender: TObject);
var
Header: THeader;
HeaderItem: THeaderItem;
I: Integer;
begin
inc(FCount);
Text1.Text:= Format('Executed [%.3d]', [FCount]);
Header:= THeader((Sender as TStringGrid).FindStyleResource('header'));
if Assigned(Header) then
begin
for I := 0 to pred(Header.Count) do
begin
HeaderItem:= Header.Items[I];
HeaderItem.StyledSettings := HeaderItem.StyledSettings - [TStyledSetting.Size, TStyledSetting.FontColor];
HeaderItem.Font.Size := 20;
HeaderItem.FontColor:= TAlphaColors.Blue;
HeaderItem.TextSettings.HorzAlign := TTextAlign.Center;
HeaderItem.TextSettings.VertAlign := TTextAlign.Center;
end;
Header.Height := 28;
end;
end;
end.
I can not answer "why" questions, otherwise than "by design".
But to solve your problem, call
StringGrid1.NeedStyleLookup;
after you have made your changes to the structure (number of columns / rows) of the grid.
To personnalise the header of a grid in a FireMonkey project, you can use onDrawColumnHeader event (if you prefer to draw on the Canvas) or use styles.
In the form editor, on your grid, use context menu to "change default style" or "change personalised style".
The header is in "grid style / background / header" for the text.
To personnalise the background, create a new "headeritemstyle" element (try TLayout with no HitTest), add a "background" to it (TButtonStyle, TRectangle or other) aligned as Contents with no HitTest and a TText component with StyleName "text", HitTest to False and aligned to client. You also can manage it's behaviour directly from this style element.

Delphi FMX: Saving and loading container children

Starting from this layout at design time.
(It contains several TLayout, TGridPanelLayout, TText elements as example)
At runtime, I am saving the complete objects structure to a file using ObjectBinaryToText
But when loading the file back from the file using ObjectTextToBinary, I get this result
Why the sub-controls are not taking the exqct same layout as saved before?
The file structure seems to be OK and containing all sub-controls as described when saving my form with the IDE
Here is a piece of code demonstrating the problem.
PAS File
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
system.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,
FMX.Dialogs, FMX.Objects, FMX.Layouts, FMX.Controls.Presentation,
FMX.StdCtrls;
type
TForm1 = class(TForm)
RecTop: TRectangle;
ButtonSave: TButton;
ButtonClear: TButton;
ButtonLoad: TButton;
Layout1: TLayout;
GridPanelLayout1: TGridPanelLayout;
Text1: TText;
Text2: TText;
Text3: TText;
Text4: TText;
procedure ButtonSaveClick(Sender: TObject);
procedure ButtonClearClick(Sender: TObject);
procedure ButtonLoadClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
AppPath: string;
AppDatFile: String;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
uses
System.IOUtils;
procedure TForm1.ButtonSaveClick(Sender: TObject);
var
FileStream : TFileStream;
MemStream : TMemoryStream;
begin
FileStream := TFileStream.Create(AppDatFile, fmCreate);
try
MemStream := TMemoryStream.Create;
MemStream.WriteComponent(Layout1);
MemStream.Position := 0;
ObjectBinaryToText(MemStream, FileStream);
finally
MemStream.Free;
FileStream.Free;
end;
end;
procedure TForm1.ButtonClearClick(Sender: TObject);
var
i: Integer;
begin
for i := pred(Layout1.ChildrenCount) downto 0 do
Layout1.Children[i].Free;
end;
procedure TForm1.ButtonLoadClick(Sender: TObject);
var
FileStream : TFileStream;
MemStream : TMemoryStream;
begin
if FileExists(AppDatFile) then
begin
FileStream := TFileStream.Create(AppDatFile, fmOpenRead);
try
MemStream := TMemoryStream.Create;
ObjectTextToBinary(FileStream, MemStream);
MemStream.Position := 0;
MemStream.ReadComponent(Layout1);
Layout1.Align:= TAlignLayout.Client;
finally
MemStream.Free;
FileStream.Free;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
AppPath:= TPath.GetLibraryPath;
AppDatFile:= TPath.Combine(AppPath, 'SaveLoadLayout.dat');
end;
end
FMX File
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 480
ClientWidth = 640
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnCreate = FormCreate
DesignerMasterStyle = 0
object RecTop: TRectangle
Align = Top
Size.Width = 640.000000000000000000
Size.Height = 41.000000000000000000
Size.PlatformDefault = False
end
object ButtonSave: TButton
Position.X = 8.000000000000000000
Position.Y = 8.000000000000000000
TabOrder = 3
Text = 'Save'
OnClick = ButtonSaveClick
end
object ButtonClear: TButton
Position.X = 96.000000000000000000
Position.Y = 8.000000000000000000
TabOrder = 2
Text = 'Clear'
OnClick = ButtonClearClick
end
object ButtonLoad: TButton
Position.X = 184.000000000000000000
Position.Y = 8.000000000000000000
TabOrder = 1
Text = 'Load'
OnClick = ButtonLoadClick
end
object Layout1: TLayout
Align = Client
Size.Width = 640.000000000000000000
Size.Height = 439.000000000000000000
Size.PlatformDefault = False
TabOrder = 4
object GridPanelLayout1: TGridPanelLayout
Align = Client
Size.Width = 640.000000000000000000
Size.Height = 439.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
ColumnCollection = <
item
Value = 50.000000000000000000
end
item
Value = 50.000000000000000000
end>
ControlCollection = <
item
Column = 0
Control = Text1
Row = 0
end
item
Column = 1
Control = Text2
Row = 0
end
item
Column = 0
Control = Text3
Row = 1
end
item
Column = 1
Control = Text4
Row = 1
end>
RowCollection = <
item
Value = 50.000000000000000000
end
item
Value = 50.000000000000000000
end>
object Text1: TText
Align = Client
Size.Width = 320.000000000000000000
Size.Height = 219.500000000000000000
Size.PlatformDefault = False
Text = 'Text1'
end
object Text2: TText
Align = Client
Size.Width = 320.000000000000000000
Size.Height = 219.500000000000000000
Size.PlatformDefault = False
Text = 'Text2'
end
object Text3: TText
Align = Client
Size.Width = 320.000000000000000000
Size.Height = 219.500000000000000000
Size.PlatformDefault = False
Text = 'Text3'
end
object Text4: TText
Align = Client
Size.Width = 320.000000000000000000
Size.Height = 219.500000000000000000
Size.PlatformDefault = False
Text = 'Text4'
end
end
end
end
As I said in my comment, the problem is that WriteComponent wrongly write items with the format:
Control = Form1.Text1
This is not correct, it should be
Control = Text1
The behavior is maybe caused by the fact that serializing a component using other component, their owner is saved along.
The workaround is to correct what WriteComponent write. A simple implementation using a simple ReplaceString is like this:
procedure TForm1.ButtonSaveClick(Sender: TObject);
var
StringStream : TStringStream;
MemStream : TMemoryStream;
Buf : String;
begin
MemStream := nil;
StringStream := TStringStream.Create;
try
MemStream := TMemoryStream.Create;
MemStream.WriteComponent(Layout1);
MemStream.Position := 0;
ObjectBinaryToText(MemStream, StringStream);
Buf := StringReplace(StringStream.DataString,
' Control = ' + Self.Name + '.',
' Control = ', [rfReplaceAll]);
TFile.WriteAllText(AppDatFile, Buf);
finally
MemStream.Free;
StringStream.Free;
end;
end;
Be aware that this workaround implementation works for your example but could be confused because the search and replace do not use a real parser and could replace something else having the same form (A string property for example).

TTeeGrid at runtime creation gets slower as the number of columns increases

I am creating TTeeGrid (TDataSet descendant) at runtime supplied by API. I noticed that as the number of columns increases, the performance decreases. Meaning, the time of creating TTeeGrid is getting slower.
I am developing firemonkey app here and the performance is noticeable in iOS and Android when it reach to 20 columns or more.
Here's my code:
procedure TformMain.btnCreateTeeGridClick(Sender: TObject);
begin
FreeAndNil(CanvassGrid); // delete the old grid
// create a new grid
CanvassGrid := TTeeGrid.Create(recCanvass);
With CanvassGrid do
begin
Parent := recCanvass;
Align := TAlignLayout.Client;
Margins.Top := 5;
Margins.Left := 5;
Margins.Right := 5;
Margins.Bottom := 5;
ScrollBars.Visible := True;
Header.Format.Font.Size := 11;
Cells.Format.Font.Size := 11;
TabOrder := 0;
ScrollBars.Visible := False;
end;
end;
Is there a way that I can improve the performance or did I missed something in my code that causes the performance to slow?
UPDATE 1: Minimal Reproducible Example
FMX File
object Form9: TForm9
Left = 0
Top = 0
Caption = 'MRE TeeGrid Runtime'#13#10
ClientHeight = 480
ClientWidth = 294
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
DesignerMasterStyle = 0
object btn1: TButton
Align = Bottom
Position.Y = 440.000000000000000000
Size.Width = 294.000000000000000000
Size.Height = 40.000000000000000000
Size.PlatformDefault = False
TabOrder = 9
Text = 'CREATE TEEGRID'
OnClick = btn1Click
end
object aniSearchProcess: TAniIndicator
Position.X = 128.000000000000000000
Position.Y = 216.000000000000000000
end
object lyt1: TLayout
Align = Client
Size.Width = 294.000000000000000000
Size.Height = 440.000000000000000000
Size.PlatformDefault = False
TabOrder = 11
end
object cur1: TFDGUIxWaitCursor
Provider = 'FMX'
Left = 32
Top = 32
end
object dvr1: TFDPhysSQLiteDriverLink
Left = 88
Top = 32
end
object con1: TFDConnection
Params.Strings = (
'DriverID=SQLite')
Connected = True
LoginPrompt = False
Left = 144
Top = 32
end
object loc1: TFDLocalSQL
Connection = con1
Active = True
Left = 200
Top = 32
end
object rsc1: TRESTClient
Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,'
AcceptCharset = 'utf-8, *;q=0.8'
BaseURL =
'https://me6hwinr2k.execute-api.ap-southeast-1.amazonaws.com/v0/d' +
'bqueries?item-var=9&qty=25'
Params = <>
Left = 32
Top = 112
end
object rsq1: TRESTRequest
Client = rsc1
Params = <>
Response = rsp1
SynchronizedEvents = False
Left = 32
Top = 176
end
object rsp1: TRESTResponse
ContentType = 'application/json'
Left = 32
Top = 240
end
object rsd1: TRESTResponseDataSetAdapter
Active = True
Dataset = mtb1
FieldDefs = <>
Response = rsp1
Left = 32
Top = 304
end
object mtb1: TFDMemTable
Active = True
FieldDefs = <
item
Name = 'Category'
DataType = ftWideString
Size = 255
end
item
Name = 'ID'
DataType = ftWideString
Size = 255
end
item
Name = 'Item'
DataType = ftWideString
Size = 255
end
item
Name = 'Qty'
DataType = ftWideString
Size = 255
end
item
Name = 'Container'
DataType = ftWideString
Size = 255
end
item
Name = 'Size'
DataType = ftWideString
Size = 255
end
item
Name = 'Ex temporibus dolore consequatur.'
DataType = ftWideString
Size = 255
end
item
Name = 'Et cum aut est nostrum...'
DataType = ftWideString
Size = 255
end
item
Name = 'Sequi quibusdam eum.'
DataType = ftWideString
Size = 255
end>
IndexDefs = <>
FetchOptions.AssignedValues = [evMode]
FetchOptions.Mode = fmAll
ResourceOptions.AssignedValues = [rvSilentMode]
ResourceOptions.SilentMode = True
UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
UpdateOptions.CheckRequired = False
UpdateOptions.AutoCommitUpdates = True
StoreDefs = True
Left = 32
Top = 368
end
end
FMX Procedures
unit Main;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FireDAC.UI.Intf, FireDAC.FMXUI.Wait, FireDAC.Stan.ExprFuncs,
FireDAC.Phys.SQLiteDef, FireDAC.Stan.Intf, FireDAC.Stan.Option,
FireDAC.Stan.Error, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool,
FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.SQLite, Data.DB,
FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, REST.Types,
FMX.Controls.Presentation, FMX.StdCtrls, FireDAC.Comp.DataSet,
FireDAC.Comp.Client, REST.Response.Adapter, REST.Client, Data.Bind.Components,
Data.Bind.ObjectScope, FireDAC.Phys.SQLiteVDataSet, FireDAC.Comp.UI,
FMXTee.Control, FMXTee.Grid, FMX.Layouts;
type
TForm9 = class(TForm)
cur1: TFDGUIxWaitCursor;
dvr1: TFDPhysSQLiteDriverLink;
con1: TFDConnection;
loc1: TFDLocalSQL;
rsc1: TRESTClient;
rsq1: TRESTRequest;
rsp1: TRESTResponse;
rsd1: TRESTResponseDataSetAdapter;
mtb1: TFDMemTable;
btn1: TButton;
aniSearchProcess: TAniIndicator;
lyt1: TLayout;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form9: TForm9;
tgd1: TTeeGrid;
implementation
{$R *.fmx}
procedure TForm9.btn1Click(Sender: TObject);
var
i, CanvassItemId, e : Integer;
begin
aniSearchProcess.Visible := True;
aniSearchProcess.Enabled := True;
{$IFDEF MSWINDOWS}
Application.ProcessMessages;
{$ENDIF}
{$IF DEFINED(iOS) or DEFINED(ANDROID)}
Application.HandleMessage;
{$ENDIF}
FreeAndNil(tgd1); //free old grid
//create new grid
tgd1 := TTeeGrid.Create(lyt1);
With tgd1 do
begin
Parent := lyt1;
Align := TAlignLayout.Client;
Margins.Top := 5;
Margins.Left := 5;
Margins.Right := 5;
Margins.Bottom := 5;
ScrollBars.Visible := True;
Header.Format.Font.Size := 11;
Cells.Format.Font.Size := 11;
TabOrder := 0;
ScrollBars.Visible := False;
end;
con1.StartTransaction;
try
//define the API here for duplicate/update, initial click and subsequent clicks
rsc1.BaseURL := ...;
rsq1.Execute;
rsd1.Active := True;
mtb1.Active;
tgd1.DataSource := mtb1;
tgd1.Enabled := True;
// adjust the column properties dynamically
with tgd1 do
begin
for i := 0 to Columns.Count -1 do
begin
if i = 0 then
begin
Columns[i].Visible := False; // category column
end
else if (i = 1) then
begin
Columns[i].Visible := False; // id column
end
else if (i = 2) then
begin
Columns[i].Width.Value := 120; // item column
end
else if (i = 3) then
begin
Columns[i].Width.Value := 30; // qty column
end
else if (i = 4) then
begin
Columns[i].Width.Value := 50; // container column
end
else if (i = 5) then
begin
Columns[i].Width.Value := 50; // size column
end
else
begin
Columns[i].Width.Value := 50; // subsequent random columns
end;
end;
end;
finally
con1.Commit;
end;
aniSearchProcess.Visible := False;
aniSearchProcess.Enabled := False;
{$IFDEF MSWINDOWS}
Application.ProcessMessages;
{$ENDIF}
{$IF DEFINED(iOS) or DEFINED(ANDROID)}
Application.HandleMessage;
{$ENDIF}
end;
end.

How to access nested style control

I have custom styled FireMonkey control. Its style contains several levels of nested controls.
I need to access those controls and change some style properties at run-time. To do that I am using FindStyleResource<T> method.
I have no problem in retrieving first level of controls inside style. But accessing controls on second level with FindStyleResource fails if control parent is descendant of TStyledControl.
Question is how to access those nested style controls regardless of their parent type?
Style:
object TStyleContainer
object TLayout
StyleName = 'MyHeader'
Align = Center
Size.Width = 100.000000000000000000
Size.Height = 50.000000000000000000
Size.PlatformDefault = False
Visible = False
TabOrder = 0
object TLabel
StyleName = 'title'
Align = Client
StyledSettings = [Style]
Size.Width = 36.000000000000000000
Size.Height = 50.000000000000000000
Size.PlatformDefault = False
TextSettings.HorzAlign = Center
Text = 'Title'
end
object TLayout
StyleName = 'green'
Align = MostLeft
Size.Width = 32.000000000000000000
Size.Height = 50.000000000000000000
Size.PlatformDefault = False
object TPath
StyleName = 'greenpath'
Align = Fit
Data.Path = {
07000000000000000000404100000041010000000000C0400000604101000000
B81EED405C8F76410100000000004041AE472D410100000052B884415C8F7641
010000000000904100006041030000000000404100000041}
Fill.Color = claGreen
HitTest = False
Size.Width = 32.000000000000000000
Size.Height = 50.000000000000000000
Size.PlatformDefault = False
Stroke.Kind = None
WrapMode = Fit
end
end
object TSpeedButton
StyleName = 'red'
Align = MostRight
Position.X = 68.000000000000000000
Size.Width = 32.000000000000000000
Size.Height = 50.000000000000000000
Size.PlatformDefault = False
object TPath
StyleName = 'redpath'
Align = Fit
Data.Path = {
07000000000000000000404100000041010000000000C0400000604101000000
B81EED405C8F76410100000000004041AE472D410100000052B884415C8F7641
010000000000904100006041030000000000404100000041}
Fill.Color = claRed
HitTest = False
Size.Width = 32.000000000000000000
Size.Height = 32.571426391601560000
Size.PlatformDefault = False
Stroke.Kind = None
WrapMode = Fit
end
end
end
end
Control:
type
TMyHeader = class(TStyledControl)
protected
procedure ApplyStyle; override;
function GetDefaultStyleLookupName: string; override;
end;
procedure TMyHeader.ApplyStyle;
var
LGreen: TLayout;
LGreenPath: TPath;
LRed: TSpeedButton;
LRedPath: TPath;
begin
inherited;
if FindStyleResource<TLayout>('green', LGreen) then
begin
// following call will find greenpath control
if FindStyleResource<TPath>('greenpath', LGreenPath) then
LGreenPath.Fill.Color := TAlphaColorRec.Blue;
end;
if FindStyleResource<TSpeedButton>('red', LRed) then
begin
// following call will fail to find find redpath control
if FindStyleResource<TPath>('redpath', LRedPath) then
LRedPath.Fill.Color := TAlphaColorRec.Blue;
// this variant also fails
if LRed.FindStyleResource<TPath>('redpath', LRedPath) then
LRedPath.Fill.Color := TAlphaColorRec.Blue;
end;
end;
function TMyHeader.GetDefaultStyleLookupName: string;
begin
Result := 'MyHeader';
end;
Original style:
Changed style (only green arrow color was successfully changed)
In ApplyStyle method I can access greenpath from the style and change its color to blue. Hoewever, I cannot get redpath using FindStyleResource method.
The standard way to access style elements is via TFMXObject and iterate the children style objects.
Try this:
procedure TMyHeader.ApplyStyle;
var
objFMX,
inObjFMX: TFMXObject;
LRed: TSpeedButton;
LRedPath: TPath;
begin
inherited;
objFMX:=FindStyleResource('red');
if assigned(objFMX) and (objFMX is TSpeedButton) then
begin
LRed:=objFMX as TSpeedButton;
inObjFMX:=LRed.FindStyleResource('redpath');
if assigned(inObjFMX) and (inObjFMX is TPath) then
begin
LRedPath:=inObjFMX as TPath;
LRedPath.Fill.Color := TAlphaColorRec.Blue;
end;
end
end;
Updated Code: The FindStyleResource does not work in the above code. A different approach is followed below.
procedure TMyHeader.ApplyStyle;
var
objFMX,
inObjFMX: TFMXObject;
LRedPath: TPath;
begin
inherited;
objFMX:=FindStyleResource('red');
if assigned(objFMX) and (objFMX is TSpeedButton) then
begin
for inObjFMX in objFMX.Children do
begin
if inObjFMX is TPath then
begin
LRedPath:=inObjFMX as TPath;
LRedPath.Fill.Color:=TAlphaColorRec.Blue;
Break;
end;
end;
end;
end;
That works on 10.2

Resources