FireMonkey tRectangle with tLabel child - delphi

I am coding a custom control based on tRectangle:
tMyRect = class (tRectangle)
On the tMyRect Constructor, I create a tLabel:
fRectLabel := tLabel.Create (Self);
and then set some properties for it.
At runtime, the tLabel is not showed according to the properties settings, neither responds to the speedkey.
Follows the complete code:
unit frmMyRect;
interface
uses FMX.Controls, FMX.Controls.Presentation, FMX.Forms, FMX.Layouts,
FMX.Objects, FMXFMX.StdCtrls, FMX.Types,System.Classes, System.UITypes;
type
tfrmMyRect = class (tForm)
procedure FormCreate (Sender: tObject);
end;
tMyRect = class (tRectangle)
fRectLabel : tLabel;
constructor Create (aOwner: tComponent);
end;
var formMyRect: tfrmMyRect;
implementation
{$R *.fmx}
var MyRect : tMyRect;
procedure tformMyRect.FormCreate (Sender: tObject);
begin
MyRect := tMyRect.Create (Self);
MyRect.Parent := frmMyRect;
end; { FormCreate }
constructor tMyRect.Create (aOwner: tComponent);
begin
inherited;
Align := tAlignLayout.Center;
CanFocus := True;
Height := 23;
Width := 80;
fRectLabel := tLabel.Create (Self);
with fRectLabel do begin
Align := tAlignLayout.Center;
AutoSize := True;
FocusControl := Self;
HitTest := True;
Parent := Self;
Text := 'Labe&l';
with TextSettings do begin
FontColor := TAlphaColorRec.Blue;
WordWrap := False;
Font.Style := [TFontStyle.fsBold];
end;
end;
end; { Create }
end.
I appreciate if someone can clarify why the tLabel does not behave as expected.

You need to alter the StyleSettings property of the TLabel so that the styling system does not apply those that you have changed, e.g.:
StyledSettings := StyledSettings - [TStyledSetting.FontColor, TStyledSetting.Style];
As to the "neither responds to the speedkey" part, you'll need to clarify what you mean, as you have not shown code related to that

Related

Delphi FMX TTreeView Argument out of range exception

Using Delphi 10.4.
I am hoping someone can explain what I am doing wrong with my FMX TTreeView that is causing an EArgumentOutOfRangeException. I am trying to create a custom TTreeViewItem class that allows me to associate some data with each node, as well as provide an in-place editor to allowing changing the node text.
The code below is a stripped down version of what I am doing. The FMX form has a TTreeview and two buttons on it, with the form's Onshow set to FormShow and the buttons set to the two button events.
The TVLinkTreeViewItem is my custom TTreeViewItem where I add a background and edit component for my in-place editor, which is displayed when a node is double clicked.
When you run the code as is, the program will throw the exception when the logic gets to the TreeView1.EndUpdate call at the end of the FormShow routine. The exception is thrown in FMX.Controls in the TControl.EndUpdate procedure.
If you comment out the ExpandAll call, the exception is not thrown, but if you mess with the expanding and collapsing of the nodes and resizing of the form, sooner or later the exception gets thrown. I left the ExpandAll line in the code below, as I assume the exception is being caused by the same error.
From what I can tell, the problem appears to be how I am setting up the fBackground and fEditor. If I don't call the AddObject routine and not set the Parent properties, I get no exception.
So can anybody tell me what I am doing wrong? Or is there a better way to do an in-place editor for the FMX TTreeViewItems component?
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.TreeView, FMX.Layouts, FMX.Controls.Presentation,
FMX.MultiView, FMX.Edit, FMX.Objects, FMX.StdCtrls;
type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
Button2: TButton;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
type
TVLinkTreeViewItem = class(TTreeViewItem)
private
fData: string;
fEditor: TEdit;
fBackground: TRectangle;
procedure TreeViewItem1DblClick(Sender: TObject);
procedure EditorExit(Sender: TObject);
procedure EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
public
property Editor: TEdit read fEditor write fEditor;
property Data: string read fData write fData;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.ExpandAll;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
TreeView1.CollapseAll;
end;
procedure TForm1.FormShow(Sender: TObject);
var
I, c, r, s: Integer;
vNode1,
vNode2,
vNode3,
vNode4: TVLinkTreeViewItem;
begin
TreeView1.BeginUpdate;
TreeView1.Clear;
for I := 0 to 4 do
begin
vNode1 := TVLinkTreeViewItem.Create(TreeView1);
vNode1.Text := 'Level 1 - '+ IntToStr(I);
TreeView1.AddObject(vNode1);
for c := 0 to 4 do
begin
vNode2 := TVLinkTreeViewItem.Create(vNode1);
vNode2.Text := 'Level 2 - '+ IntToStr(c);
vNode1.AddObject(vNode2);
for r := 0 to 4 do
begin
vNode3 := TVLinkTreeViewItem.Create(vNode2);
vNode3.Text := 'Level 3 - '+ IntToStr(r);
vNode2.AddObject(vNode3);
// for s := 0 to 4 do
// begin
// vNode4 := TVLinkTreeViewItem.Create(vNode3);
// vNode4.Text := 'Level 4 - '+ IntToStr(s);
// vNode3.AddObject(vNode4);
// end;
end;
end;
end;
//ExpandAll works when no parent is set for fBackGround and fEditor is not set in "TVLinkTreeViewItem.Create" below"
//If the Parents are set below, ExpandAll/EndUpdate causes "Augument out of range" exception.
TreeView1.ExpandAll;
treeView1.EndUpdate;
end;
{ TVLinkTreeViewItem }
constructor TVLinkTreeViewItem.Create(AOwner: TComponent);
begin
inherited;
fData := '';
fBackground := TRectangle.Create(AOwner);
//When ExpandAll is not called in FormShow,
// Calling "AddObject" or setting parent, as shown below, make all the code work,
// but will get intermident "Augument out of range" exceptions when resizing form,
// or when expanding or collapsing nodes using the buttons.
self.AddObject(fBackGround);
//fBackGround.Parent := self;
fBackGround.Visible := false;
fEditor := TEdit.Create(AOwner);
fBackGround.AddObject(fEditor);
//fEditor.Parent := fBackGround;
fEditor.Visible := false;
fEditor.Align := TAlignLayout.Client;
fEditor.OnKeyDown := EditorKeyUp;
self.OnDblClick := TreeViewItem1DblClick;
fEditor.OnExit := EditorExit;
end;
destructor TVLinkTreeViewItem.Destroy;
begin
inherited;
end;
procedure TVLinkTreeViewItem.TreeViewItem1DblClick(Sender: TObject);
begin
fBackGround.Visible := true;
fBackGround.Width := self.Width - 20;
fBackGround.Height := self.Height;
fBackGround.Position.X := 20;
fEditor.Enabled := true;
fEditor.Visible := true;
fEditor.Opacity := 1;
fBackGround.BringToFront;
fEditor.BringToFront;
fEditor.Text := Text;
fEditor.SetFocus;
fEditor.SelectAll;
end;
procedure TVLinkTreeViewItem.EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
begin
inherited;
if Key = vkReturn then
begin
Text := fEditor.Text;
fBackGround.Visible := false;
fEditor.Enabled := false;
end
else if Key in [vkEscape, vkCancel, vkTab, vkHardwareBack] then
begin
fBackGround.Visible := false;
fEditor.Enabled := false;
end;
end;
procedure TVLinkTreeViewItem.EditorExit(Sender: TObject);
begin
fBackGround.Visible := false;
fEditor.Enabled := false;
fEditor.Visible := false;
end;
end.
Here's the fmx content:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 480
ClientWidth = 640
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnShow = FormShow
DesignerMasterStyle = 0
object TreeView1: TTreeView
Align = Left
Size.Width = 269.000000000000000000
Size.Height = 480.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
Viewport.Width = 265.000000000000000000
Viewport.Height = 476.000000000000000000
end
object Button1: TButton
Position.X = 356.000000000000000000
Position.Y = 68.000000000000000000
TabOrder = 2
Text = 'Expand'
OnClick = Button1Click
end
object Button2: TButton
Position.X = 354.000000000000000000
Position.Y = 102.000000000000000000
TabOrder = 1
Text = 'Collapse'
OnClick = Button2Click
end
end

Stack Overflow error on adding component to form

Hi I have just completed a component which implements a Table search on typing in an edit box and showing the results in a drop down dbCtrlGrid. It compiles and installs without any problem. But when placed on a form Delphi (7) I get a Stack Overflow save your work and restart Delphi. I cannot debug it as it is not on the form so can anyone help please?
unit QueryPnl;
interface
uses WinTypes, WinProcs, Messages, SysUtils, Classes, Controls,
Forms, Graphics, Extctrls, Eedit, Stdctrls, ABSMain,
Db, DBCtrls, EDBEdit, dbcgrids;
type
TQueryPanel = class(TPanel)
private
Addressmem : TDBMemo;
Display : TDBCtrlGrid;
DsQ1 : TDataSource;
Head : TLabel;
FDbase : TABSDatabase;
FTableName : string;
FOnInTextChange : TNotifyEvent;
procedure AutoInitialize;
procedure AutoDestroy;
protected
InText : TEedit;
NmText : TEDBEdit;
NumText : TEDBEdit;
Q1 : TABSQuery;
procedure InTextChange(Sender : TObject); overload;
procedure DoEnter; override;
procedure DoExit; override;
procedure Click; override;
procedure KeyPress(var Key : Char); override;
procedure Loaded; override;
procedure Paint; override;
function GetFDbase : TABSDatabase;
procedure SetFDbase(Value : TABSDatabase);
function GetFTableName : string;
procedure SetFTableName(Value : string);
public
procedure InTextChange;overload;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
property DBase : TABSDatabase read FDBase write SetFDBase;
property TableName : String read FTableName write SetFTableName;
property OnInTextChange : TNotifyEvent read FOnInTextChange write FOnInTextChange;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Data Controls', [TQueryPanel]);
end;
procedure TQueryPanel.AutoInitialize;
begin
Addressmem.Parent := Display;
with Addressmem do
begin
Left := 2;
Top := 50;
Width := 115;
Height := 50;
DataField := 'Address';
TabOrder := 1;
end;
Display.Parent := Self;
with Display do
begin
DataSource:=DsQ1;
Left := 0;
Top := 54;
Width := 138;
Height := 315;
Color := $00E8DBCE;
PanelHeight := 105;
PanelWidth := 121;
ParentColor := False;
TabOrder := 2;
end;
DsQ1.DataSet:=Q1;
Head.Parent := Self;
with Head do
begin
Left := 1;
Top := 1;
Width := 136;
Height := 13;
Align := alTop;
Alignment := taCenter;
Caption := 'Quick Name Search';
Font.Charset := DEFAULT_CHARSET;
Font.Color := clRed;
Font.Height := -12;
Font.Name := 'MS Sans Serif';
Font.Style := [fsBold];
ParentFont := False;
end;
InText.Parent := Self;
with InText do
begin
Left := 0;
Top := 25;
Width := 121;
Height := 21;
TabOrder := 0;
OnChange := InTextChange;
UpCaseFirst := True;
ColorOnFocus := clYellow;
end;
NmText.Parent := Display;
with NmText do
begin
Left := 2;
Top := 8;
Width := 115;
Height := 21;
DataField := 'Name';
Font.Charset := DEFAULT_CHARSET;
Font.Color := clWindowText;
Font.Height := -11;
Font.Name := 'MS Sans Serif';
Font.Style := [fsBold];
ParentFont := False;
TabOrder := 0;
end;
NumText.Parent := Display;
with NumText do
begin
Left := 1;
Top := 29;
Width := 115;
Height := 21;
DataField := 'Number';
Font.Style := [fsBold];
TabOrder := 2;
end;
Q1.DatabaseName:=FDBase.Name;
Q1.RequestLive:=True;
end;
procedure TQueryPanel.AutoDestroy;
begin
Addressmem.Free;
Display.Free;
DsQ1.Free;
Head.Free;
InText.Free;
NmText.Free;
NumText.Free;
Q1.Free;
end;
procedure TQueryPanel.DoEnter;
begin
inherited DoEnter;
Height := 370;
end;
procedure TQueryPanel.DoExit;
begin
inherited DoExit;
Height := 55;
end;
function TQueryPanel.GetFDbase : TABSDatabase;
begin
Result := FDbase;
end;
procedure TQueryPanel.SetFDBase(Value : TABSDatabase);
begin
FDBase := Value;
// Other code to do when selecting the database
end;
function TQueryPanel.GetFTableName : string;
begin
Result := FTableName;
end;
procedure TQueryPanel.SetFTableName(Value : String);
begin
FTableName := Value;
// Other code to do when selecting the table
end;
procedure TQueryPanel.InTextChange(Sender : TObject);
begin
if Assigned(FOnInTextChange) then
FOnInTextChange(Sender);
Q1.Close;
Q1.SQL.Text:='select * from '+FTableName+' where Name like :nem';
Q1.ParamByName('nem').asString:=InText.Text;
Q1.Open;
end;
procedure TQueryPanel.Click;
begin
inherited Click;
end;
procedure TQueryPanel.KeyPress(var Key : Char);
const
TabKey = Char(VK_TAB);
EnterKey = Char(VK_RETURN);
begin
inherited KeyPress(Key);
end;
constructor TQueryPanel.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Addressmem := TDBMemo.Create(Self);
Display := TDBCtrlGrid.Create(Self);
DsQ1 := TDataSource.Create(Self);
InText := TEedit.Create(Self);
Head := TLabel.Create(Self);
NmText := TEDBEdit.Create(Self);
NumText := TEDBEdit.Create(Self);
Q1 := TABSQuery.Create(Self);
AutoInitialize;
end;
destructor TQueryPanel.Destroy;
begin
inherited Destroy;
end;
procedure TQueryPanel.InTextChange;
begin
//
end;
procedure TQueryPanel.Loaded;
begin
inherited Loaded;
end;
procedure TQueryPanel.Paint;
begin
inherited Paint;
end;
end.
Many times, StackOverflow errors are due to an infinite recursion.
I think the code of these two procedures causes that:
procedure SetDBase(Value : TABSDatabase);
procedure SetTableName(Value : String);
Use Field like FDBase and FTableName to store the values (for example).
The 2 methods are causing the Set method to be called again infinitely.
In the private section, add this:
FDBase : TABSDataBase;
FTableName : String;
In the published section, add:
property DBase : TABSDataBase read FDBase write SetDBase;
property TableName : String read FTableName write SetTableName;
In the implementation, write:
procedure TQueryPanel.SetDBase(Value : TABSDatabase);
begin
FDBase := Value;
// Other code to do when selecting the database
end;
procedure TQueryPanel.SetTableName(Value : String);
begin
FTableName := Value;
// Other code to do when selecting the table
end;
Where I put comments "Other code...", if there is nothing to do, you don't need the setter at all.
Please keep attention that fields begin by letter F and properties don't.
I tracke the error - it was in the TQueryPanel.AutoInitialize;
I guess that the line
Q1.DatabaseName:=FDBase.Name;
was in the wrong place so I put in
procedure TQueryPanel.SetDbase(Value : TABSDatabase);
begin
FDbase:= Value;
Q1.DatabaseName:=FDBase.DatabaseName;
end;
and it works fine now.Thank you everyone for your help and suggestions
Actually it doesn't. The first panel of the DBCtrlGrid is fine but apparently you have to have the DBCtrlGrid.Panel as parent which is reserved and cannot be accessed. Shame it seemed an ideal solution in a limited space.

Toggle Delphi Label color with Firemonkey

With VCL I can do this:
procedure TForm1.FormCreate(Sender: TObject);
begin
Label1.Color := clBlue;
Label1.Font.Color := clYellow;
label1.Caption := ' My Label '
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Label1.Color = clBlue then
begin
Label1.Color := clYellow;
Label1.Font.Color := clBlue
end
else
begin
Label1.Color := clBlue;
Label1.Font.Color := clYellow
end
end;
As you can see, the color of the label background and the text toggles from blue to yellow and vice-versa.
I want to do the same with Firemonkey but, all the search I made only says that FMX labels has no background color (I don't understand why),
and don't give me a effetive clue how to do the same thing as in VCL.
Can someone write here the equivalent FMX code snippet?
Thank you.
In Firemonkey many controls do not have a color. Instead you're supposed to layer controls out of different components.
In this case if you want a background use a TRectangle.
In the designer Delphi insists that you cannot have a label be parented by a rectangle, but this if of course not true, in FMX any control can parent any other.
Just use the structure pane to drag the label on top of the rectangle and voila label and rectangle are joined together.
The equivalent code to the above would look something like this.
unit Unit45;
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;
type
TForm45 = class(TForm)
Rectangle1: TRectangle;
Label1: TLabel;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
Rects: array of TRectangle;
Labels: array of TLabel;
public
{ Public declarations }
end;
var
Form45: TForm45;
implementation
{$R *.fmx}
uses
System.UIConsts;
procedure TForm45.Button1Click(Sender: TObject);
var
i: integer;
begin
for i:= Low(Rects) to High(Rects) do begin
if Rects[i].Fill.Color <> claBlue then
Rects[i].Fill.Color:= claBlue
else Rects[i].Fill.Color:= claYellow;
end;
end;
procedure TForm45.FormCreate(Sender: TObject);
var
i: integer;
begin
SetLength(Rects,2);
SetLength(Labels,2);
for i:= 0 to 1 do begin
Rects[i]:= TRectangle.Create(self);
Rects[i].Parent:= self;
Labels[i]:= TLabel.Create(self);
Labels[i].Parent:= Rects[i];
Rects[i].Width:= Rectangle1.Width;
Rects[i].Height:= Rectangle1.Height;
Rects[i].Position.y:= 0 + i * Rects[i].Height;
Rects[i].Position.x:= 0 + i * Rects[i].Width;
Rects[i].Stroke.Kind:= TBrushKind.None;
Labels[i].AutoSize:= true;
Labels[i].Text:= 'Test'+IntToStr(i+1);
Labels[i].Position:= Label1.Position;
end;
end;
end.
Note that I've done the construction of the labels and rects in runtime, but you can do this in design time as well.
The color constants in FMX have changed from the VCL, see: http://docwiki.embarcadero.com/RADStudio/Seattle/en/Colors_in_FireMonkey
As an alternative, you can create a custom style for your TLabel component:
right-click the Label ("LabelXX") and select "Edit Custom Style...";
add a "TRectangle" component from the "Tool Palette" to the new style created ("LabelXXStyle1");
select the new "Rectangle1Style" object and send it to back (Edit -> "Send to Back");
Set the "Rectangle1Style" properties:
"Align" : "Client";
"Fill / Bitmap / Color" : any background color;
Apply changes (close the "Style Designer").
Set the "StyleLookup" property of the other TLabel(s) you need to "LabelXXStyle1".
If you are interested, here is one of my samples:
function CreateLabel(
AOwner: TFmxObject; ARangeWidth, ARangeHeight, ASizeMin, ASizeMax: Integer;
AText: String; AColor: TAlphaColor): TLabel;
var
LFMXObj: TFMXObject;
LFontSize: Integer;
begin
Result := TLabel.Create(AOwner);
with Result do
begin
Parent := AOwner;
Text := AText;
ApplyStyleLookup;
LFMXObj := FindStyleResource('text');
if Assigned(LFMXObj) then
begin
LFontSize := ASizeMin + Random(ASizeMax - ASizeMin);
//TText(LFMXObj).Fill.Color := AColor; // XE2
TText(LFMXObj).Color := AColor;
TText(LFMXObj).Font.Size := LFontSize;
TText(LFMXObj).Font.Style := TText(LFMXObj).Font.Style + [TFontStyle.fsBold];
TText(LFMXObj).WordWrap := False;
TText(LFMXObj).AutoSize := True;
Canvas.Font.Assign(TText(LFMXObj).Font);
Position.X := Random(ARangeWidth - Round(Canvas.TextWidth(Text)));
Position.Y := Random(ARangeHeight - Round(Canvas.TextHeight(Text)));
end;
{
// test background label painting
with TRectangle.Create(Result) do
begin
Parent := AOwner;
Fill.Color := TAlphaColors.Lightgrey;
Fill.Kind := TBrushKind.bkSolid;
Width := Result.Canvas.TextWidth(Result.Text);
Height := Result.Canvas.TextHeight(Result.Text);
Position.X := Result.Position.X;
Position.Y := Result.Position.Y;
Result.BringToFront;
end;
}
AutoSize := True;
Visible := True;
end;
end;

How to create a dialog like component that allows drop other controls inside it?

It is a Firemonkey component, however I could see that most of the component base is the same for VCL and FMX, so please if you know how to do that in VCL share your knowledge, it can be eventually the solution for my case.
I am using a TPopup as the ancestor. It is convenient for me since it remains on the form/frame and I can wire it with LiveBindings using the same context/structure of the parent, this is very convenient for me.
I need it behave exactly it is the TPopup, as a container. But I need it looks better and have my specific buttons (I have created some properties and automations for my software inside it)
The problem is that I create some internal controls, like TLayouts, Tpanels and Tbuttons to make looks like this: (empty)
That black area inside it is where I want to drop controls like TEdit and others.
I have set all the internal created controls to Store = false, so it is not getting stored on the streaming system. Doing that when I drop a TEdit for example, what I get is this (Tedit with aligned=top I need this):
However I was expecting this:
If I change the Store = true I can get the right effect, but all the inside controls are exposed on the Structure panel and every time I save the form and reopen everything gets duplicated. The inside components exposed is not a problem for me, but the duplication is, if I close and open the component 10 times I will get the entire inside structure replicated 10 time.
I will try to show some code that is related to the design of the component:
Class declaration:
[ComponentPlatformsAttribute(pidWin32 or pidWin64 or pidOSX32 or pidiOSSimulator or pidiOSDevice or pidAndroid)]
TNaharFMXPopup = class(TPopup, INaharControlAdapter, INaharControl)
private
protected
FpnlMain : TPanel;
FlytToolBar : TLayout;
FbtnClose : TButton;
FbtnSave : TButton;
FbtnEdit : TButton;
FpnlClientArea : TPanel;
FlblTitle : TLabel;
procedure Loaded; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
constructor Create:
constructor TNaharFMXPopup.Create(AOwner: TComponent);
begin
inherited;
FpnlMain := TPanel.Create(Self);
FlblTitle := TLabel.Create(Self);
FlytToolBar := TLayout.Create(Self);
FbtnEdit := TButton.Create(Self);
FpnlClientArea := TPanel.Create(Self);
FbtnClose := TButton.Create(FlytToolBar);
FbtnSave := TButton.Create(FlytToolBar);
Height := 382;
Placement := TPlacement.Center;
StyleLookup := 'combopopupstyle';
Width := 300;
ApplyControlsProp;
end;
Setting properties of the internal controls:
procedure TNaharFMXPopup.ApplyControlsProp;
begin
with FpnlMain do
begin
Parent := Self;
Align := TAlignLayout.Client;
StyleLookup := 'grouppanel';
TabOrder := 0;
Margins.Bottom := 10;
Margins.Left := 10;
Margins.Right := 10;
Margins.Top := 10;
Stored := false;
end;
with FlblTitle do
begin
Parent := FpnlMain;
Text := 'Título';
Align := TAlignLayout.Top;
Height := 36;
StyleLookup := 'flyouttitlelabel';
Stored := false;
end;
with FpnlClientArea do
begin
Parent := FpnlMain;
Align := TAlignLayout.Client;
StyleLookup := 'gridpanel';
TabOrder := 0;
Margins.Bottom := 5;
Margins.Left := 5;
Margins.Right := 5;
Margins.Top := 5;
Stored := false;
end;
with FlytToolBar do
begin
Parent := FpnlMain;
Align := TAlignLayout.Bottom;
Height := 50;
Stored := false;
end;
with FbtnClose do
begin
Parent := FlytToolBar;
Text := 'Fecha';
Align := TAlignLayout.Left;
Height := 50;
StyleLookup := 'tilebutton';
TabOrder := 0;
Width := 70;
ModalResult := mrClose;
Stored := false;
end;
with FbtnEdit do
begin
Parent := FlytToolBar;
Text := '';//'Edita';
Align := TAlignLayout.Left;
Height := 50;
StyleLookup := 'tilebutton';
TabOrder := 1;
Width := 70;
ModalResult := mrContinue;
Stored := false;
Enabled := false;
end;
with FbtnSave do
begin
Parent := FlytToolBar;
Text := 'Salva';
Align := TAlignLayout.Left;
Height := 50;
StyleLookup := 'tilebutton';
TabOrder := 2;
Width := 70;
ModalResult := mrOk;
Stored := false;
end;
end;
Loaded:
procedure TNaharFMXPopup.Loaded;
begin
inherited;
ApplyControlsProp;
SetEvents;
end;
I have tried the following with notification, trying to make the inserted control a parent for my intenal "clientarea"
procedure TNaharFMXPopup.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opInsert) and (csDesigning in ComponentState) then
begin
if AComponent.Owner = self then
if AComponent is TFmxObject then
begin
(AComponent as TFmxObject).Parent := FpnlClientArea;
end;
end;
end;
But that made nothing change.
I have asked similar question before, but I was not aware of many things on creating such a component and the answer I got gave little help, I was missing the Parent of each internal component.
Now I am trying to really show where is my need: I need to drop controls on my TPopup dialog that will be parented of the ClientArea inside it.
Take a closer look at TTabControl / TTabItem in the unit FMX.TabControl. This is your perfect example because it basically needs to solve the same problem.
The following function is what you need to override:
procedure DoAddObject(const AObject: TFmxObject); override;
This is called when a control is added to your control. Override this function so that your control is added to the FpnlClientArea control instead. You'd get something similar to this:
procedure TNaharFMXPopup.DoAddObject(const AObject: TFmxObject);
// ...
begin
if (FpnlClientArea <> nil) and not AObject.Equals(FpnlClientArea) and not AObject.Equals(ResourceLink) then
begin
FpnlClientArea.AddObject(AObject);
end
else
inherited;
end;
Make sure that AObject.Equals also excludes your other "not stored" controls.
Without the DoAddObject override, the FMX TabControl would show the same problem as your component currently has.
The TPopup is not intended to accept controls. So that needs a few more tricks.
Here's a modified version of your unit that works for me. I've added a few comments:
unit NaharFMXPopup;
interface
uses
System.UITypes,
System.Variants,
System.SysUtils, System.Classes, FMX.Types, FMX.Controls, FMX.Layouts, FMX.StdCtrls;
type
[ComponentPlatformsAttribute(pidWin32 or pidWin64 or pidOSX32 or pidiOSSimulator or pidiOSDevice or pidAndroid)]
TNaharFMXPopup = class(TPopup)
private
procedure ApplyControlsProp;
protected
FpnlMain : TPanel;
FlytToolBar : TLayout;
FbtnClose : TButton;
FbtnSave : TButton;
FbtnEdit : TButton;
FpnlClientArea : TContent; // change to TContent.
// For TPanel we'd have to call SetAcceptControls(False),
// but that is not easily possible because that is protected
FlblTitle : TLabel;
procedure Loaded; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure DoAddObject(const AObject: TFmxObject); override;
public
procedure InternalOnClose(Sender: TObject);
procedure InternalOnSave(Sender: TObject);
procedure InternalOnEdit(Sender: TObject);
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetEvents;
published
end;
implementation
{ TNaharFMXPopup }
constructor TNaharFMXPopup.Create(AOwner: TComponent);
begin
inherited;
FpnlMain := TPanel.Create(Self);
FlblTitle := TLabel.Create(Self);
FlytToolBar := TLayout.Create(Self);
FbtnEdit := TButton.Create(Self);
FpnlClientArea := TContent.Create(Self); // change to TContent
FbtnClose := TButton.Create(FlytToolBar);
FbtnSave := TButton.Create(FlytToolBar);
Height := 382;
Placement := TPlacement.Center;
StyleLookup := 'combopopupstyle';
Width := 300;
// A TPopup is not intended to accept controls
// so we have to undo those restrictions:
Visible := True;
SetAcceptsControls(True);
ApplyControlsProp;
end;
destructor TNaharFMXPopup.Destroy;
begin
inherited;
end;
procedure TNaharFMXPopup.ApplyControlsProp;
begin
with FpnlMain do
begin
Parent := Self;
Align := TAlignLayout.Bottom;
StyleLookup := 'grouppanel';
TabOrder := 0;
Height := 50;
Margins.Bottom := 10;
Margins.Left := 10;
Margins.Right := 10;
Margins.Top := 10;
Stored := false;
end;
with FpnlClientArea do
begin
Parent := Self; // we have to change this to Self (it refuses working if the parent is FPnlMain)
Align := TAlignLayout.Client;
Margins.Left := 3;
Margins.Right := 3;
Margins.Top := 3;
Margins.Bottom := 3;
Stored := false;
end;
with FlytToolBar do
begin
Parent := FpnlMain;
Align := TAlignLayout.Bottom;
Height := 50;
Stored := false;
end;
with FbtnClose do
begin
Parent := FlytToolBar;
Text := 'Close';
Align := TAlignLayout.Left;
Height := 50;
StyleLookup := 'tilebutton';
TabOrder := 0;
Width := 70;
ModalResult := mrClose;
Stored := false;
end;
with FbtnEdit do
begin
Parent := FlytToolBar;
Text := '';//'Edita';
Align := TAlignLayout.Left;
Height := 50;
StyleLookup := 'tilebutton';
TabOrder := 1;
Width := 70;
ModalResult := mrContinue;
Stored := false;
Enabled := false;
end;
with FbtnSave do
begin
Parent := FlytToolBar;
Text := 'Save';
Align := TAlignLayout.Left;
Height := 50;
StyleLookup := 'tilebutton';
TabOrder := 2;
Width := 70;
ModalResult := mrOk;
Stored := false;
end;
end;
procedure TNaharFMXPopup.Loaded;
begin
inherited;
ApplyControlsProp;
// SetEvents;
end;
procedure TNaharFMXPopup.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
end;
procedure TNaharFMXPopup.InternalOnClose(Sender: TObject);
begin
end;
procedure TNaharFMXPopup.InternalOnEdit(Sender: TObject);
begin
end;
procedure TNaharFMXPopup.InternalOnSave(Sender: TObject);
begin
end;
procedure TNaharFMXPopup.SetEvents;
begin
FbtnClose.OnClick := InternalOnClose;
FbtnSave.OnClick := InternalOnSave;
FbtnEdit.OnClick := InternalOnEdit;
end;
procedure TNaharFMXPopup.DoAddObject(const AObject: TFmxObject);
begin
//inherited; try commenting the block bellow and uncommenting this one
//Exit;
if (FpnlClientArea <> nil)
and not AObject.Equals(FpnlClientArea)
and not AObject.Equals(ResourceLink)
and not AObject.Equals(FpnlMain)
and not AObject.Equals(FlblTitle)
and not AObject.Equals(FlytToolBar)
and not AObject.Equals(FbtnEdit)
and not AObject.Equals(FpnlClientArea)
and not AObject.Equals(FbtnClose)
and not AObject.Equals(FbtnSave) then
begin
FpnlClientArea.AddObject(AObject);
end
else
inherited;
end;
end.

How to make my own dialog component from Firemonkey TPopUp?

[Delphi XE5 Up2]
I am trying to use TPopUp to inherit and create a component, following the same idea as exposed on the Flyouts demo for the CalendarFlyout. I will be not using the Calendar, but I want that space free so that I can place any other FMX component that I want.
I have made the component using the new component wizard and added some controls:
unit PopupTest;
interface
uses
System.SysUtils, System.Classes, FMX.Types, FMX.Controls,
FMX.Layouts, FMX.StdCtrls;
type
TPopupTest = class(TPopup)
private
FPanel : TPanel;
FLayoutButton : TLayout;
FCloseButton : TButton;
FSaveButton : TButton;
FClientArea : TLayout;
protected
procedure OnClose(Sender: TObject);
procedure OnSave(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TPopupTest]);
end;
{ TPopupTest }
constructor TPopupTest.Create(AOwner: TComponent);
begin
inherited;
FPanel := TPanel.Create(self);
FPanel.Position.X := 0;
FPanel.Position.Y := 0;
FPanel.Margins.Left := 10;
FPanel.Margins.Right := 10;
FPanel.Margins.Top := 10;
FPanel.Margins.Bottom := 10;
FPanel.StyleLookup := 'flyoutpanel';
FPanel.Align := TAlignLayout.alClient;
FPanel.Visible := True;
FLayoutButton := TLayout.Create(FPanel);
FLayoutButton.Align := TAlignLayout.alBottom;
FLayoutButton.Height := 22;
FCloseButton := TButton.Create(FLayoutButton);
FCloseButton.Align := TAlignLayout.alLeft;
FCloseButton.StyleLookup := 'flyoutbutton';
FCloseButton.Text := 'Fechar';
FCloseButton.OnClick := OnClose;
FSaveButton := TButton.Create(FLayoutButton);
FSaveButton.Align := TAlignLayout.alLeft;
FSaveButton.StyleLookup := 'flyoutbutton';
FSaveButton.Text := 'Salvar';
FSaveButton.OnClick := OnSave;
FClientArea := TLayout.Create(FPanel);
FClientArea.Align := TAlignLayout.alClient;
Width := 100;
Height := 100;
end;
destructor TPopupTest.Destroy;
begin
FClientArea.Free;
FCloseButton.Free;
FSaveButton.Free;
FLayoutButton.Free;
FPanel.Free;
inherited;
end;
procedure TPopupTest.OnClose(Sender: TObject);
begin
end;
procedure TPopupTest.OnSave(Sender: TObject);
begin
end;
end.
I have made several tests and nothing appears, just the popup itself, nothing inside. I am using the MetropoliUI style and the Styles on the component for the inner controls are based on that style.
For simplicity I have remove everything else and compiled and tested.
I am using the TPopUp for several reasons, but the main one is that my "dialog" will be inserted on the form, and I will add to it some TEdits that will be connected by LiveBinding to the same DataSet etc on the form. So no need to create another form with everything else, and preserve all the context (at least I believe this is the right thing to do)
What I am looking for:
What is missing to make all the internal controls appear
How to make sure that the FClientArea, that is a TLayout will be available for the user to add other controls on it?
The final result would like this:
Where in the middle area is a TLayout where I could drop other controls like TEdit.
When you create the TPopupTest in your form you have to set the creator's owner to your Form, as well as the Parent.
Changing the Unit to something like this will make it appear but it's not exactly as you picture it, you will have to refine it a bit. Also my solution might not be the best but at least you will get to see something now.
constructor TPopupTest.Create(AOwner: TComponent);
var
PopPanel: TPanel;
PopLayout: TLayout;
PopClose: TButton;
PopSave: TButton;
PopClientArea: TLayout;
begin
inherited;
PopPanel := TPanel.Create(Owner);
PopPanel.Position.X := 0;
PopPanel.Position.Y := 0;
PopPanel.Margins.Left := 10;
PopPanel.Margins.Right := 10;
PopPanel.Margins.Top := 10;
PopPanel.Margins.Bottom := 10;
PopPanel.StyleLookup := 'flyoutpanel';
PopPanel.Parent := Owner as TFmxObject;
PopPanel.Align := TAlignLayout.alClient;
PopPanel.Visible := True;
PopLayout := TLayout.Create(Owner);
PopLayout.Parent := PopPanel;
PopLayout.Align := TAlignLayout.alBottom;
PopLayout.Height := 22;
PopClose := TButton.Create(Owner);
PopClose.Parent := PopLayout;
PopClose.Align := TAlignLayout.alLeft;
PopClose.StyleLookup := 'flyoutbutton';
PopClose.Text := 'Fechar';
PopClose.OnClick := OnClose;
PopSave := TButton.Create(Owner);
PopSave.Parent := PopLayout;
PopSave.Align := TAlignLayout.alLeft;
PopSave.StyleLookup := 'flyoutbutton';
PopSave.Text := 'Salvar';
PopSave.OnClick := OnSave;
PopClientArea := TLayout.Create(Owner);
PopClientArea.Parent := PopPanel;
PopClientArea.Align := TAlignLayout.alClient;
FPanel:= PopPanel;
FLayoutButton:= PopLayout;
FSaveButton:= PopSave;
FCloseButton:= PopClose;
FClientArea:= PopClientArea;
Width := 100;
Height := 100;
end;

Resources