How to assign a component to a Delphi GridPanel row and column programmatically? - delphi

I am working on a project using Delphi where I am making rows and columns for a GridPanel dynamically. I am also creating a panel (TPanel) that I intend to place in each cell dynamically.
My question is: How do you assign the newly created panel to a particular cell that is in the GridPanel?
I am guessing that I have to assign the panel's "Parent" property so that it is the GridView. However, I have not been able to adjust a "Row" or "Column" property for the TPanel unlike when you able to when you assign a panel to the GridPanel using the design environment.

How do you assign the newly created panel to a particular cell that is
in the GridPanel?
The answer is, you can't. Adding new components to the TGridPanel, wether at design time or programmatically, places the new component in the next unoccupied cell. By default, in left to right order.
The ExpandStyle property (emAddRows or emAddColumns) determines how the TPanelGrid is expanded when filled, and further controls are added.
After you have added the new panel (or other component) to the TGridPanel you may change it's position as others have answered, by accessing the control via the ControlCollection property.

Use:
TGridPanelLayout.ControlCollection[position].row := 0;

You can access the property
TGridPanelLayout.ControlCollection[index].row := 0;
and
TGridPanelLayout.ControlCollection[index].Column := 0;

Related

How do you right justify DBGrid titles when using a FDMemtable in Delphi?

I can't seem to get the fixed row Titles in a DBGrid to align right justified when using a FDMemtable. Whenever I set the Field alignment to taRightJustify it right justifies the data cells perfectly. However, the DBGrid titles are always left justified.
What's even more frustrating is I can set the corresponding DBGrid column title alignment to taRightJustify and it appears perfectly fine in the IDE. But when I run the program the column title shows as left justified.
Has anyone found a way to make DBGrid column titles stay right justified when using a FDMemtable?
BTW, this also happens with taCenter. The data cells align centered but the titles stay left justified.
PEBKAC
The issue was of my own making. I did not invoke the DBGrid Columns Editor and add all the fields. Instead, I was using the "Structure" pane and getting to the DBGrid columns that way. Although the Structure pane allowed me to modify the column titles this was only temporary and did not persist when the program was run.
I can't reproduce the issue using a TFDMemTable either.
I dropped a TFDMemTable, TDataSource, and TDBGrid on a new VCL application's main form, connected them as usual (the grid's datasource set to DataSource1, the datasource's DataSet set to FDMemTable1), and then added the below code to the form's OnCreate event:
procedure TForm3.FormCreate(Sender: TObject);
begin
FDMemTable1.FieldDefs.Add('ID', ftInteger, 0, True);
FDMemTable1.FieldDefs.Add('LastName', ftString, 20);
FDMemTable1.FieldDefs.Add('FirstName', ftString, 20);
FDMemTable1.FieldDefs.Add('Salary', ftCurrency);
FDMemTable1.CreateDataSet;
FDMemTable1.Active := True;
FDMemTable1.AppendRecord([1, 'Smith', 'John', 30000]);
FDMemTable1.AppendRecord([2, 'Jones', 'Jane', 40000]);
FDMemTable1.AppendRecord([3, 'Doe', 'David', 2500]);
DBGrid1.Columns[3].Alignment := TAlignment.taRightJustify;
DBGrid1.Columns[3].Title.Alignment := TAlignment.taRightJustify;
end;
It also works correctly if I set everything up at designtime. Repeat the same setup steps I used above, but instead of using the code, use the following steps:
Select FDMemTable1 in the Object Inspector. At the bottom of the OI, click the LoadFromFile link, and navigate to the BDS Samples data folder (by default, in C:\Users\Public\Public Documents\Embarcadero\Studio\17.0\Samples\Data) and select animals.fds. (No specific reason for choosing that one, except it has a numeric field we can use for testing.)
Right-click on the DBGrid, and choose Columns Editor, or click the ellipsis button on the DBGrid.Columns property in the Object Inspector. Right-click in the Columns Editor and choose Add all fields.
Select either the Size or Weight column, expand it's Title property, and set Alignment to taRightJustify.
Run the application. The column you modified in step #3 above has a right-aligned title. (Here I used the Size column.)
The code below works for me in Seattle. I'm using a TClientDataSet rather than a TFDMemTable, but I can't see that that would make any difference.
If you have persistent columns defined on your DBGrid, you can also set a column's title alignment via the Object Inspector - use it to select the column, then expand its Title node and you can set the title alignment there.
procedure TForm1.CDS1AfterOpen(DataSet: TDataSet);
var
i : Integer;
begin
for i := 0 to DBGrid1.Columns.Count - 1 do
DBGrid1.Columns[i].Title.Alignment := taRightJustify;
end;
Btw, if you think you're setting the alignment in the OI but it's getting ignored, see if you can find out why, as follows:
Make sure your form is saved, then right-click on it and select View as text. Then, in the IDE editor window, you can see whether the Alignment property is saved as you've specified in the OI. Use the editor context menu to return to viewing the form as a form.
Add an override of the form's Loaded method as I've described in a comment. With a breakpoint on the inherited in Loadeds body, you can inspect the Alignment value before and after inherited is called.
Why am I suggesting looking into Loaded? Well, it is called after a form is streamed in from the DFM and is the routine where the run-time system finishes setting up the form. Sometimes, admittedly very rarely, another component (usually a 3rd-party one) misbehaves and causes strange behaviour of the properties of other compononents.

Positioning of custom list box item components in Delphi XE5, Firemonkey

I've customised the style of a Firmeonkey list box item in such a way that now it can consist of 4 TLables in it. Each of the lable has Alignment as alNone.
I'm setting position of each of them in my code whenever i need to add any item. I've observed that when my list has scroll bar and if first component is not visible (i.e. i've scrolled down enough) at that time if i re-add all the items again in list box, then the position of TLabels in first items (or items which are not shown) get distorted.
For setting positions I am using below code :
(tmpListBoxItem.FindStyleResource('txtCol2') As TLabel).Position.X :=
(tmpListBoxItem.FindStyleResource('txtCol2') As TLabel).Position.X + (tmpListBoxItem.FindStyleResource('txtCol2') As TLabel).Width;
Any suggesstions, how can i overcome this issue.
Regards,
Padam Jain
Firemonkey styles are repeatedly 'applied' and 'freed' as components appear and disappear from screen.
It is not enough to simply set properties of style objects once and expect those values to be remembered. What you need to do is either listen to the OnApplyStyleLookup event or override the ApplyStyle method of a custom component and use the same you have above to set the properties again.
This means you'll need somewhere to store the values you are going to set.
I would suggest for your situation that you subclass TListBoxItem so you can add suitable properties or fields and put your code in ApplyStyle.

What control should I use to create this UI in Delphi Firemonkey

I am developing an application for mobile (android and ios) by Delphi xe5.
I am willing to create this UI:
I tried TListBox but image on left and right cant be set.
I tried TListView but same problem as TListBox
I tried TGrid with custom column, The problem of texts and images is solved but I can't create headers of each rows (it hasn't something like colspan)
What I need is to create a custom control and repeat it.
What is the best solution?
Any solution or guide line will be appreciated.
Solution
Thanks #Mike Sutton for answer, this is the result
The style here is so different from a standard TListBoxItem style that is probably makes sense to start from scratch, in which case the issues with accessing the default styles become immaterial.
Add a TStyleBook to your form.
Set the StyleBook property of the form to point to it.
Double click the icon to open the editor.
Drag a TLayout to the structure panel and drop it on the only item which will be there.
Set the StyleName property of the TLayout (e.g. ScoreListBoxItemStyle).
Drag/drop other components to build up the layout you want (remember TLayouts for 'hidden' positioning).
Set the StyleName property of any components you want reference from your code.
Subclass TLIstBoxItem to TScoreListBoxItem (if using the StyleName suggested above).
Add properties for your text, images etc.
In the setter methods for each of these, cache the data and call a method such as:
procedure SetFlag1;
var O: TFMXObject;
begin
O := FindStyleResource('flag1'); //StyleName of the item
if O is TImage then
TImage(O).Bitmap.Assign(FFlag1);
end;
Override the ApplyStyle method and call all of your methods that set the data in the style.
Now create your items in code:
Item := TScoreListBoxItem.Create(Self);
ListBox1.AddObject(Item);
Item.Flag1.LoadFromReource ...
...
Here's an idea that I don't have time to test:
Create a descendant of a TListBoxItem and in that add you two images as normal TImages. I'm pretty sure that a TListBoxItem can parent an object. You'll have to place the images on the listbox item where you want them. Then whenever you add an item to the listbox item just pass in your own descendant.
(If this doesn't work someone let me know and I'll delete this.)

FireMonkey grid with different controls in same column

What can you do if you want different cell controls in the same column of a grid in FireMonkey.
The cell control seems to belong to the column, but there are situations (like the property editor), where some rows need a checkbox while other rows need a combobox or an edit control.
Thanks in advance.
The following steps should get you up and running:
Create a style, add to it the controls you want to show (i.e. a TCheckbox, a TCombobox and a TEdit). Add these within a TLayout, and set each controls StyleName to something memorable.
In your cells ApplyStyle use FindStyleResource to extract the controls you added above using their StyleNames.
When the grid calls your cell's SetData method, you need to set the Visible property for each control so only the appropriate one is shown. If you can't determine this from the data passed in, add an event handler to the cell to get the data.
You'll need to sort out the keyboard handling, which gets pretty messy. If memory serves, you need to pass keys from the grid/cell to the control (or or is it trap movement keys from the controls and pass them to the grid? Sorry if I forget exact detail).
Sorry I can't give a more detailed answer, but covering this completely would take a whole series of blog posts.
Use a style - set the cell style when you set the cell data - then us the onapplystyle event to do anything clever you require with the newly styled cell.
This way you can add what controls you need to the style and then access the controls (to set events etc) with the onapplystyle.
Hint - FindStyleResource is your friend here :-)
I have also needed a property editor and looked for a way hosting different cell types in one column. Using different styles for each row may be a solution as suggested above, but since Firemonkey grid doesnt reserve any cell control for a specific row, each time the cell control would be shown on the row, the true style would be applied to it. This is not a big problem for a static property editor, however for a real grid which has got may rows and different cell types in each row a diferent strategy is needed. So I came up with a different solution, I considered cell type proxies between TColumn and cell controls, so that each cell proxy will reserve the cell controls that is responsible for. First of all, I have a new TColumn (TvariantColumn) which is responsible for the top strategy.
vColumn := TVariantColumn.Create(Self);
vColumn.Header := 'Variant Column';
vColumn.OnGetCellProxyIndex := GetCellProxyIndex;
Grid1.AddObject(vColumn);
Then create any proxies like
vColumn.NewCellProxy(TTextProxy);
vColumn.NewCellProxy(TColorComboProxy);
vColumn.NewCellProxy(TComboColorProxy);
You can also handle Proxy specific jobs after you create it, like
with TProgressProxy(vColumn.NewCellProxy(TProgressProxy)) do //4
begin
Min := 0;
Max := 100;
end;
with TPopUpProxy(vColumn.NewCellProxy(TPopupProxy)) do //5
begin
Items.Add('Istanbul');
Items.Add('Paris');
Items.Add('NewYork');
end;
I have blogged my method in my website and published a detailed article where you can find more about the subject.

How can I make a component disappear in the form designer?

I'm creating a custom control that contains other controls, including two scroll bars, one horizontal, one vertical. There's a property to set one or both scroll bars invisible, which works fine at runtime, but at design-time it doesn't disappear from the form designer when I set the property.
Now, I can understand how this could be a useful feature if this was an individual control, but it's part of an aggregate component and so it ought to obey the parent control's rules, not the form designer's. Is there any way to make sure the control will disappear when I set .Visible := false; even when it's in the form designer?
You can set the "csNoDesignVisible" ControlStyle flag of the control. If that flag is set the Visible property will work as it does at runtime.

Resources