Custom TListBox Style Firemonkey - delphi

I have followed the sample supplied by EMB that can be found on
"C:\Users\Public\Documents\Embarcadero\Studio\14.0\Samples\Object Pascal\FireMonkey Desktop\CustomListBox"
this is how it looks like:
This is the code that add the row to the listbox:
procedure TfrmCustomList.Button2Click(Sender: TObject);
var
Item: TListBoxItem;
begin
// create custom item
Item := TListBoxItem.Create(nil);
Item.Parent := ListBox1;
Item.StyleLookup := 'CustomItem';
Item.Text := 'item ' + IntToStr(Item.Index); // set filename
if Odd(Item.Index) then
Item.ItemData.Bitmap := Image1.Bitmap // set thumbnail
else
Item.ItemData.Bitmap := Image2.Bitmap; // set thumbnail
Item.StylesData['resolution'] := '1024x768 px'; // set size
Item.StylesData['depth'] := '32 bit';
Item.StylesData['visible'] := true; // set Checkbox value
Item.StylesData['visible.OnChange'] := TValue.From<TNotifyEvent>(DoVisibleChange); // set OnChange value
Item.StylesData['info.OnClick'] := TValue.From<TNotifyEvent>(DoInfoClick); // set OnClick value
end;
As you can see there is this line where the custom style is applyed:
Item.StyleLookup := 'CustomItem';
There is a StyleBook on the form, and the form is associated to it. The TListBox has not style applied.
However if you change this CustomItem Style, nothing happens. You can even change the names of the itens and nothing happens (not visual change at all) the layout keeps fixed as it is shown on the windows above.
I have added another TTlabel with its own name and tried to assign to it:
Item.StylesData['ghost'] := 'scary thing';
It does not give any error, but no text is shown. The style keeps immutable.
If removed the Item.StyleLookup assignment when creating the item the only thing changed is that the name of the TLabel are lost and then there is no way to assign the value.
So, the style is defined, but I see somehow it seems to be fixed. Any layout change is not applied, somehow seems to only understand the style sub items name changing. That is not useful at all.
How can I truly modify this style? I want to put each of the 3 TLabels side by side in a horizontal layout.

While I understand and share your frustration this problem appears to be your own doing.
However if you change this CustomItem Style, nothing happens.
There are three reasons why this might happen:
You have multiple Stylebook components and you are not editing the active one.
StyleLookup doesn't match any style name in the StyleBook
You copied the example project to a new location and failed to realize that you are still modifying the original files.
I'm quite positive that it's the second reason.
If your Delphi IDE is set to Autosave Project desktop then a .dsk file is generated when you close your project.
When you reopen the project later, the .dsk file is read, and your desktop layout, your breakpoints, and your watches are all restored. Also, all files that were opened when the project was closed are opened again, regardless of whether they are used by the project.
Here is the example with labels in a horizontal layout:
You can download the example from dropbox and examine it to see where you went wrong.

Related

Is there a simple way for temporary hiding the Teechart BackImage?

I'm looking for a simple way (if exist) for temporary hiding (and then show it again) of Theechart BackImage.
Something like:
Chart1.Backimage.Visible := false; // then true
I know how to change the back image by code from a stream or file, such as:
Chart1.BackImage.LoadFromFile(<An image file name>);
But wonder if there is a simple way to do it.
No easy hide/show/visible etc. To hide the image you can set the style to custom and set the bounds / positions to not show anything. To bring it back set to what is was previously, for example tile and all 0's.
Chart1.BackImage.Mode := pbmCustom; // In the UI this is Style
Chart1.BackImage.Left := 0;
Chart1.BackImage.Right := 1;
Chart1.BackImage.Top := 0;
Chart1.BackImage.Bottom := 1;
To see properties and values set things manually in the IDE design time on a form and then view the form as text to see what the properties and values are.
Set Chart1.BackImage to nil in order to remove it.

How can I add a caption/text to my csExDropDownList Combobox in Delphi?

I'm currently trying to create a combobox that has a "caption". By caption I mean the text that you see when you haven't clicked on it yet or if you are choosing an option.
Before I added the csExDropDownList it worked fine, but I wanted it to be ReadOnly. Now when I edit the Text property, it instantly gets deleted. I thought about using a TLabel in front of the combobox and making it dissapear the moment I chose a dropdown, but the TLabel is always in the background. I also tried with the TStaticText component, but that leaves a different colored background in front of the combobox which looks bad.
If I was unable to explain with words what I'm trying to edit/wanted to add a text to, this is what I mean:
I've found a workaround to my problem. I added a third dropdown with the index of 0. Now in properties I put the ItemIndex to 0 which means that it will be displayed similar to the Text property. When I interact with either QuickSort or InsertSort I delete Index 0.
My code looks like this:
procedure TSorterForm.AlgorithmCbxChange(Sender: TObject);
begin
if (AlgorithmCbx.Text <> 'Choose Algorithm...') and not IsAlgorithmSelected then begin
AlgorithmCbx.Items.Delete(0);
IsAlgorithmSelected:= true;
end;
end;
Obviously not perfect so it'd be great if you could tell me how to improve this.

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.

Why doesn't my run-time-created component appear on the form?

I am testing the example that came from this Q&A Component Creation - Joining Components Together? to learn how to create a custom/composite component.
While the installed component from the example works dragging on to the form, I can't seem to create it at run time.
procedure TForm1.Button1Click(Sender: TObject);
var
MyPanel2 : TMyPanel;
begin
MyPanel2 := TMyPanel.Create(Form1);
With MyPanel2 do
begin
Left := 10;
Top := 10;
Width := 400;
Height := 400;
Visible := True;
Image.Picture.LoadFromFile('C:\test.png');
end;
end;
I tried both self and Form1 as the owner. Played with properties of both the panel and the image.
Just not sure what I am doing wrong. No errors except when I forgot to add pngimage to my uses. Steps through the code just fine, nothing visually occurs for the run time creation.
You need to set Parent in the runtime code.
MyPanel2 := TMyPanel.Create(Self);
with MyPanel2 do
begin
Parent := Self;//oops, you forgot to set this
SetBounds(10, 10, 400, 400);
Image.Picture.LoadFromFile('C:\test.png');
end;
The code in your question won't result in the control showing for a plain vanilla TPanel, or indeed any control.
From the documentation, with my emphasis:
Specifies the parent of the control.
Use the Parent property to get or set the parent of the control. The
parent of a control is the control that contains it. For example, if
an application includes three radio buttons in a group box, the group
box is the parent of the three radio buttons, and the radio buttons
are the child controls of the group box.
To serve as a parent, a control must be an instance of a TWinControl
descendant.
When creating a new control at run time, assign a Parent property
value for the new control. Usually, this is a form, panel, group box,
or a control that is designed to contain another. Changing the parent
of a control moves the control onscreen so that it is displayed within
the new parent. When the parent control moves, the child moves with
the parent.

Font consistency throughout Project?

Is there a quick and effective way of applying a global Font to be used in a project?
By this I mean I would like to set a specific Font name that all controls in my project will use such as TButton, TEdit, TLabel etc.
Typically setting the Font for the Form rather than a specific control will change all the controls on that Form to the Font specified.
There is a slight issue with this however, if you have manually changed a Font on a specific control, then setting the Font by the Form will no longer update those controls that have previously been changed manually.
Idea 1
I was thinking of using a For loop and iterating through each component on my Forms and setting the Font this way, such as:
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
with TForm(Self) do
begin
for i := 0 to ComponentCount - 1 do
begin
if Components[i] is TButton then
begin
TButton(Components[i]).Font.Name := 'MS Sans Serif';
TButton(Components[i]).Font.Size := 8;
TButton(Components[i]).Font.Style := [fsBold];
end;
if Components[i] is TLabel then
begin
TLabel(Components[i]).Font.Name := 'MS Sans Serif';
TLabel(Components[i]).Font.Size := 8;
TLabel(Components[i]).Font.Style := [fsBold];
end;
end;
end;
end;
But doing this seems very messy, it will also be a considerable amount of code for a simple task.
Idea 2
I know I could manually change the fonts at design time one by one for each control, but with several forms to go through this could take time and even then I might of missed a control.
Idea 3
Similar to Idea 2, another way could be to view the Form as Text (DFM) and Find and Replace the font that way.
Basically I am going for consistency within my Application, and having one Font used throughout is what I was looking to achieve.
Am I missing something completely obvious here, is what I am trying to do overkill for such a task?
As discussed in the comments, the key to this is the ParentFont property. This property is defined at various points in the VCL hierarchy. If you set ParentFont to be True for all components in the chain, then you can change the fonts for the entire application simply by modifying
Application.DefaultFont
By default most components set ParentFont to True and so you have nothing to do. The odd one out though is TForm. A brand new default form has ParentFont set to False. This is somewhat disappointing but I suspect reflects the fact that the original designers of the VCL did not anticipate this and that ParentFont was grafted on relatively late in the development of the VCL.
No matter, in an ideal world, all forms in your application should be derived from a common base class that you control. If that is so then you can simply make the change there, set ParentFont to be True, make sure no explicit font settings are applied to any components on you forms, and you are golden. Control the entire application's fonts through a single property. If you don't have a common base class for your forms, here's an ideal time to add it. If you don't want to do that then you need to set ParentFont for each form.
Other related properties are Screen.MessageFont and Screen.MenuFont. These provide global control over the fonts used in message boxes and menus. However, recent versions of Delphi have handed back to Windows control over the painting of message boxes and menus and so these properties have no effect.
The real key, as was mentioned, is to ensure that all your forms descend from your own application base form.
Then, you can view each form and button etc, and review the properties, where any modified font property should be displayed in bold, and is easily identified. Most properties have a "Revert to inherited" menu choice. This should essentially undo any previous selection, without having to go to the text version for editting. (Although it probably does exactly that, deleting any text entry resulting from previous font-setting).
I would definitely want to fix each form once rather than leaving it defined incorrectly and adding more code to fix it at runtime. That change will leave you with a worse problem if you later decide to do something different.
If you want to do this runtime, like you describe it in Idea 1, you should consider making it a recursive function, like this:
procedure SetFontProperties(Control: TControl; Name: TFontName; Size: Integer; Styles: TFontStyles);
// Set font properties
var
Index: Integer;
Font: TFont;
AnObject: TObject;
ChildControl: TControl;
begin
// Set font properties
AnObject := GetObjectProp(Control, 'Font', nil);
if AnObject is TFont then
begin
// Set properties
Font := TFont(AnObject);
Font.Name := Name;
Font.Size := Size;
Font.Style := Styles;
end;
// Set child font properties
if Control is TWinControl then
begin
// Set
for Index := 0 to TWinControl(Control).ControlCount - 1 do
begin
// Child control
ChildControl := TWinControl(Control).Controls[Index];
// Set font properties
SetFontProperties(ChildControl, Name, Size, Styles);
end;
end;
end;
You can then switch fonts for all controls within a form by using it like this:
SetFontProperties(Self, 'Courier', 14, []);
The function will then set the font properties of the form, and the font properties of all child controls on the form, including controls nested within TPanels or other container controls.
However I do agree with you that it's sort of a half messy way of doing it.

Resources