How to get to the elements on a page control? - delphi

I'm working with Delphi 7.
Suppose I have a page control on my form. This page control has two or three tabs. Each tab has a few other controls like a label, edit etc. on it. How to get, for example, to an edit's text property in the code?

Iterate across the controls of the tabsheet using its ControlCount and Controls properties.
for i := 0 to TabSheet.ControlCount-1 do
begin
if TabSheet.Controls[i] is TEdit then
ShowMessage(TEdit(TabSheet.Controls[i]).Text);
end;
This will iterate over all immediate children of the tabsheet. If you need to iterate deeper into the children of the children and so on then you want a recursive solution.
If you want to search in each tabsheet then you need to iterate over them too.
for i := 0 to PageControl.PageCount-1 do
TabSheet := PageControl.Pages[i];
for j := 0 to TabSheet.ControlCount-1 do
begin
if TabSheet.Controls[j] is TEdit then
ShowMessage(TEdit(TabSheet.Controls[j]).Text);
end;

You can still directly access the TEdit
Edit1.Text := 'My Edit box on a Tab';

Related

Procedure to change the color of multiple panels with different names in Delphi

I have a TForm with 5 TPanel controls on it. I need them to change color when they are clicked on.
I can easily write code for each TPanel to change colors, but that consists of too much code, so I was wondering if I could write a procedure to change the color of each one independently and just call this function with the name of each TPanel as a parameter? If not, then is there any other way to do this?
This is really easy.
Create a new VCL application.
Add five TPanel controls to the main form.
Select all five panels.
In the Object Inspector, set ParentBackground to False.
In the Object Inspector, click the Events tab and click the empty field to the right of the OnClick row caption. Type PanelClick and press Enter.
Write the following code:
procedure TForm1.PanelClick(Sender: TObject);
begin
if Sender is TPanel then
TPanel(Sender).Color := RGB(Random(255), Random(255), Random(255));
end;
I think this is what you asked. However, given your image, I suspect you rather would like a toggling behaviour:
procedure TForm1.PanelClick(Sender: TObject);
var
i: Integer;
begin
for i := 0 to ControlCount - 1 do
if Controls[i] is TPanel then
if Controls[i] = Sender then
TPanel(Controls[i]).Color := clHighlight
else
TPanel(Controls[i]).Color := clBtnFace;
end;
This iterates over the child controls of Self, since ControlCount and Controls mean Self.ControlCount and Self.Controls, respectively. If your target panels have a different parent (a panel, say), iterate over its children instead.
Optionally, you can give the target panels a specific Tag value (like 500) and only change the colour of such panels.
And please set BevelOuter to bvNone. We have left the 90s.

Delphi/FMX: How to add a dynamically created top-aligned component under all previously added top-aligned components, instead of second from the top?

I am making an app for Android with Delphi and FMX. In the onclick-procedure of a button I dynamically create a TPanel (with some components in it) which I then add to a TVertScrollBox. I want the TPanels to stack on top of each other, so I set the Align property to Top.
procedure TMainForm.AddGroupButtonClick(Sender: TObject);
var Group : TPanel;
begin
Group := TPanel.Create(Self);
Group.Parent := Groups; // where Groups is a TVertScrollBox on the form
Group.Align := TAlignLayout.Top;
//Then I create some other components and set their Parent to Group
end;
A user would probably expect the new TPanel to be added under all the other TPanels. However, unless there's no TPanels added previously, every new TPanel is added directly under the topmost one, i.e. second from the top.
Why is this and how do I add the new TPanel under all the previously added ones?
I saw a similar question on here, but they were using VCL, where there apparently is a Top-property you can change. There doesn't seem to be one when working with FMX components though.
When you create a new Firemonkey panel, its Position property is by default X=0, Y=0.
When you set Align := TAlignLayout.Top; it compares with previously placed components, finds that there is already a panel at Y = 0 and squeezes the new panel next below that existing panel.
To place the new panel below all other panels set
...
Group.Position.Y := 1000; // any sufficiently large value (bigger than the last panel's Y)
Group.Align := TAlignLayout.Top;
It is because when you dynamically create a TPanel it has a (0,0) position so it is upper than all other previously created panels. you could use a cheating method for newly created panel to be created below other panels. here is two simple and somehow dirty solutions.
method 1:
Group := TPanel.Create(Self);
Group.Parent := Groups; // where Groups is a TVertScrollBox on the form
Group.Align := TAlignLayout.bottom; // :D it firstly created it at bottom and then move it to top
Group.Align := TAlignLayout.Top;
method 2:
Group := TPanel.Create(Self);
Group.Parent := Groups; // where Groups is a TVertScrollBox on the form
Group.Position.Y:=Groups.Height+1;
Group.Align := TAlignLayout.Top;
I hope it could solve your problem.

How do I tile MDI children and maintain BorderStyle = bsNone?

I am hoping someone can help
I have created an object that has a form. I instantiate this object during runtime. The user can then instantiate a second (or multiple) object(s) via a menu step. So two child forms now exist in a parent form. Each objects form's BorderStyle is set to none and Style is set to MDIChild during form creation at runtime:
BorderStyle := bsNone;
FormStyle := fsMDIChild;
All fine. Form has no border.
Now I want to tile the two forms created. Menu > click Tile
procedure TMainForm.Tile1Click(Sender: TObject);
begin
Tile;
end;
Now the border re-appears. I need a way to get rid of the border.
I have tried stepping through the collection of objects and explicitly setting the BorderStyle to none:
procedure TMainForm.Tile1Click(Sender: TObject);
var
i: byte;
begin
Tile;
for i := 0 to GraphCollection.Count-1 do
(GraphCollection.Items[i] as TGraphForm).BorderStyle := bsNone;
end;
This does not work.
Does anyone have a workaround that retains the bsNone style after either Tile or Cascade.
You can do this in another way. I do this with Tabset (TTabSet class) without using fsMDIChild(but BorderStyle = bsNone).
You can add a tab for each form you open on TabSet. By clicking any tab, you can activate or show appropriate form. And delete the appropriate tab for any form by closing it. I prepare an example and you can get it from here.
(I miss-understand the question, my answer is about simulating MDITab for MdiChild forms when the FormStyle is fsNormal and not fsMdiChild)

how to copy all the TLabels parented with a TPanel on delphi to another TPanel?

I have a TPanel on a delphi form, I want to copy all the TLabels
parented with this TPanel when i press a button and put them
in other panel.
Is there a way to do that?
Thanks.
To copy the TLabel controls from one TPanel to another you can use something like this
Procedure CopyLabels(ParentControl,DestControl:TWinControl);
var
i : integer;
ALabel : TLabel;
begin
for i := 0 to ParentControl.ControlCount - 1 do
if ParentControl.Controls[i] is TLabel then
begin
ALabel:=TLabel.Create(DestControl);
ALabel.Parent :=DestControl;
ALabel.Left :=ParentControl.Controls[i].Left;
ALabel.Top :=ParentControl.Controls[i].Top;
ALabel.Width :=ParentControl.Controls[i].Width;
ALabel.Height :=ParentControl.Controls[i].Height;
ALabel.Caption:=TLabel(ParentControl.Controls[i]).Caption;
//you can add manually more properties here like font or another
end;
end;
and use like this
CopyLabels(Panel1,Panel2);
you can use the RTTI too, to copy the properties from a control to another, but as you does not specify your Delphi version only i show a simple example.
TPanel is a container of Components. It has a list of its child components in its Controls property. You may iterate over this list to get access to its children.
At the press of the button your code has to
iterate on the Controls list of Panel1
check if the control is a TLabel
change the Parent property of the TLabel to be Panel2
something like this
for i := 0 to Panel1.ControlCount - 1 do
if Panel1.Controls[i] is TLabel then
(Panel1.Controls[i] as TLabel).Parent:=Panel2;
But, wait!, this will not work. Why? Because doing this change 'on the fly' you will be changing the very same list you are iterating over.
So, you have to save the labels to be moved in a temporary list. Something like this...
var
i:integer;
l:TObjectList;
begin
l:=TObjectList.Create;
l.ownsObjects:=False;
for i := 0 to Panel1.ControlCount - 1 do
if Panel1.Controls[i] is TLabel then
l.add(Panel1.Controls[i]);
for i:= 0 to l.Count-1 do
(l[i] as TLabel).Parent:=Panel2;
l.Free;
end;

Problem while using Two Forms

i don't know how to ask this question. problem is given below.
I'm using One Main form and many sub forms but not MDI Forms.
Main form Contains 5 Buttons and a Panel. each button will call a form
inside that Panel(as Parent). in that sub forms, one form(Sub3) contain TMainMenu component.
every form is working correctly while calling by clicking the buttons but, while calling the form(Sub3) the TMainMenu is not in visible. i don't know how to bring it visible.
Please help me any one.
Thanks in Advance.
Thanks & Regards,
Yuvaraj
You can only have one MainMenu on each form. While you can have multiple forms in an app each with its own MainMenu, if you show one form within another form, only the mainmenu of the "outer" form will be visible.
When you "reparent" a form to another (show formB as a "component" on formA), then you have to merge the menu's yourself as #skamradt already mentioned.
To do so, simply have your buttons use a "SwitchToForm" function like:
type
TMain_Form
...
private
FCurrentForm: TForm;
procedure SwitchToForm(showForm: TForm);
...
end;
procedure TMain_Form.SwitchToForm(showForm: TForm);
begin
if (FCurrentForm <> nil) and (FCurrentForm.Name = showForm.Name) then begin
// Naught to do
end else begin
// If a form is currently showing, hide it and if it has a menu, unmerge that
if FCurrentForm <> nil then
begin
FCurrentForm.Hide;
if Assigned(FCurrentForm.Menu) then
begin
MainMenu.UnMerge(FCurrentForm.Menu);
end;
end;
// Set the current form to the one passed in and re-parent that to the main form
// If the form has a menu, merge that with the main menu of the main form and then
// show it.
FCurrentForm := showForm;
with FCurrentForm do begin
Parent := self;
Align := alClient;
BorderIcons := [];
BorderStyle := bsNone;
end;
if Assigned(FCurrentForm.Menu) then begin
MainMenu.Merge(FCurrentForm.Menu);
end;
FCurrentForm.Show;
end;
end;
In this example: the form is parented to the main form itself, but you could of course also parent the forms to a panel or some other container on the main form.
You can use the TMainMenu.Merge and TMainMenu.Unmerge to merge/unmerge the sub-form menu with the main form menu. In your "OnActivate" for each of the child forms, I would send a custom message to the main form that would unmerge any menus from another form (which might already not be set) and merge the menu for the current form.

Resources