TChart with TKnobGauge creates shifted labels at runtime - delphi

Following code creates knob with labels shifted to the right:
procedure TForm1.FormShow(Sender: TObject);
begin
_chart := TChart.Create(Self);
_chart.Parent := Self;
_chart.Align := alClient;
_knob := TKnobGauge.Create(Self);
_knob.ParentChart := _chart;
_knob.RotateLabels := False;
_knob.RotationAngle := 180;
end;
The same code as DFM produces the right knob.
What could be wrong?
TeeChart Pro v2015.16.150901 32bit VCL
Delphi 10

There is a bug in TChart. When I set
_chart.Title.Text.Text := 'Some title';
labels are on their places.
When I do
_chart.Title.Text.Text := '';
or
_chart.Title.Visible := False;
they are shifted.
The reason why the same code in DFM produced the right knob is that the visual designer extends my minimal chart declaration by adding several properties automatically. Among these properties was a chart title too. It is automatically filled by "TChart" text.

Sounds as exactly what is described in the ticket #1547, initially reported here.
Please, give a try at the workaround described in the ticket:
I can only workaround by having a small title with only a blank in it.

Related

Dynamically assigning form size before maximize loses assigned values

I have an application which always starts initially maximized. This consists of putting Self.WindowState := wsMaximized; in the OnCreate of the main form.
Just before that, I'm assigning what should be the default dimensions of the main form, if the user were to change the window state to wsNormal.
So, in short, the main form's OnCreate handler looks something like:
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Width:= 1300;
Height:= 800;
WindowState:= wsMaximized;
end;
Theoretically, I could assign these dimensions in design-time, and that does what I need. However, due to the size of my screen, and thus the IDE, the whole form is not visible at one glance without scrolling. In design, I keep the form size small, so I can see everything. But in runtime, I need to assign these default dimensions, and then maximize it by default. When the user changes the window state out of maximized, I expect it to go to those dimensions I dynamically assigned.
The issue is that it seems to lose those dimensions after maximizing the form, and it reverts back to whatever values were in design-time. If I comment out the line WindowState:= wsMaximized; then it shows the form in the desired default dimensions. However, maximizing it seems to overwrite and ignore these values I had just assigned before it.
How can I create and show my main form maximized by default, while at the same time dynamically assigning the default size, without my assigned values getting lost?
(Confirmed with 10.3.3.)
The exact origin of this problem I cannot pinpoint, but a reasonable cause would be that during the constructor the form component is being read and that previous sizes seem to be explicitly backed up:
procedure TControl.SetWidth(Value: Integer);
begin
SetBounds(FLeft, FTop, Value, FHeight);
Include(FScalingFlags, sfWidth);
if csReading in ComponentState then
FExplicitWidth := FWidth;
end;
A possible solution is to set the desired sizes in the OnCreate event, like you are doing now, but postpone setting the desired WindowsState until the OnShow event.
procedure TForm1.FormCreate(Sender: TObject);
begin
Width := 1300;
Height := 800;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
WindowState := wsMaximized;
end;
Of course, you probably should prevent consecutive calls by using a one-off mechanism.
Please take a look at wsMaximized forms do not appear maximized too.
Apparently, the VCL does not store the explicit intermediate size (in some Delphi versions anyway) but seems to merge the change with that of the maximization when the form is actually shown.
Like Sertac Akyuz quite correctly suggested, you can use SetWindowPlacement to bypass this VCL interference:
procedure TForm1.FormCreate(Sender: TObject);
var
WindowPlacement: TWindowPlacement;
begin
GetWindowPlacement(Handle, WindowPlacement);
WindowPlacement.rcNormalPosition := Bounds(Left, Top, 1300, 800);
WindowPlacement.showCmd := SW_SHOWMAXIMIZED;
SetWindowPlacement(Handle, WindowPlacement);
end;
You must set form size on FormActivate:
procedure TfrmMain.FormActivate(Sender: TObject);
begin
if Tag = 0 then
begin
// Top := 100;
// Left := 100;
Width:= 1300;
Height:= 800;
WindowState:= wsMaximized;
Tag := 1;
end;
end;

Delphi/Firemonkey make Listboxses sync scroll better

I have a very dirty way of having 7 listboxses scroll in sync, they all have the exact same height so when I move the position of any one of them the other 6 should also move to that position
What I do is Assume Listbox1 is scrolled and ViewportPositionChange() is fired I say
Listbox2.ViewportPosition := NewViewportPosition;
Listbox3.ViewportPosition := NewViewportPosition;
And this all works and I just add this code to each Listboxses ViewportPositionChange() event, but its terribly jerky and too slow on Andriod, I still need to be able to scroll from any one listbox but is there not a way to improve the performace of the scroll?
Ok to be honest it isn't terribly slow but you can feel its not exactly snappy, and also each listbox can have lots of listboxses and each listbox can also have a few controls and more controls in those controls so alot of repainting is done I think and this is what is making it less snappy.
Sample code
Firstly place a Gridpanel layout on your form, Add 6 columns And add a listbox to each, each Listbox represents a day, where Listbox 1 is Monday, Also Align these to client.
Global variables
private Glistbox : array[0..6] of TListBox;
On form Create
Glistbox[0] := ListBox1;
Glistbox[1] := ListBox2;
Glistbox[2] := ListBox3;
Glistbox[3] := ListBox4;
Glistbox[4] := ListBox5;
Glistbox[5] := ListBox6;
Glistbox[6] := ListBox7;
And now on each listboxses OnViewportPositionChange
//Note that this would be for Listbox1 so for listbox 2 it needs to be changed acordingly
ListBox2.ViewportPosition := NewViewportPosition;
ListBox3.ViewportPosition := NewViewportPosition;
ListBox4.ViewportPosition := NewViewportPosition;
ListBox5.ViewportPosition := NewViewportPosition;
ListBox6.ViewportPosition := NewViewportPosition;
ListBox7.ViewportPosition := NewViewportPosition;
Then add a button with the following code :
var
mainlayout,item1,item2 : Tlayout;
listboxitem : TListBoxItem;
myrec,myrec2 : TRectangle;
lbl1,lbl2 : TLabel;
I: Integer;
begin
for I := 0 to 6 do
begin
listboxitem := TListBoxItem.Create(nil);
mainlayout := TLayout.Create(nil);
mainlayout.Align := TAlignLayout.Client;
mainlayout.Parent:= listboxitem;
item1 := TLayout.Create(nil);
item1.Align:= TAlignLayout.Left;
item1.Parent := mainlayout;
item2 := TLayout.Create(nil);
item2.Align:= TAlignLayout.Right;
item2.Parent := mainlayout;
myrec := TRectangle.Create(nil);
myrec.Align := TAlignLayout.Client;
myrec.Parent := item1;
myrec2 := TRectangle.Create(nil);
myrec2.Align := TAlignLayout.Client;
myrec2.Parent := item2;
lbl1 := TLabel.Create(nil);
lbl1.Align := TAlignLayout.Client;
lbl1.TextAlign := TTextAlign.Center;
lbl1.Text := '1';
myrec.AddObject(lbl1);
lbl2 := TLabel.Create(nil);
lbl2.Align := TAlignLayout.Client;
lbl2.TextAlign := TTextAlign.Center;
lbl2.Text := '2';
myrec2.AddObject(lbl2);
Glistbox[I].AddObject(listboxitem);
item2.Width:= mainlayout.Width/2;
end;
There could sometimes be more controls in a listboxitem but generally I think this would be the norm, Now add a few items and try the scroll
The FMX TListBox has a lot of overhead because every listbox item basically is a styled component that is inserted into a scrollbox. This is good for flexibility, but bad for speed.
In Windows and OSX there is functions like ScrollWindowEx or NSView.scrollRect + NSView.setNeedsDisplayInRect that can help improve performance by scrolling a part of the window without issuing paint messages. So you'd only have to invalidate a part of the listbox. I could imagine that Android has something similar. But you'll likely have to patch FMX in order to use that. This can improve performance a bit, but is still not a magic switch for great performance. A similar approach would be caching the content of the scrollbox in a bitmap and then copy parts of the bitmap to the screen while scrolling.
If you need really good listbox performance then you'll need to develop your own listbox that is optimized for speed. This is what I had to do for the OSX version of my application in order to get the performance I needed.
There's also TListView for Firemonkey which is better choice than TListBox when all items share the same visual structure. There's a tutorial by Ray Konopka (from 2014) explaining what each one is suited for:
https://www.youtube.com/watch?time_continue=934&v=XHYtwAu5fl4
For adjusting scrolling behaviour (in case it helps) see:
https://docwiki.embarcadero.com/RADStudio/Alexandria/en/FireMonkey_Layouts_Strategies#Smooth_Inertial_Scrolling

Best strategy for too many Elements in FireMoneky TListView Item [duplicate]

This question already has an answer here:
Create a customized Item Appearance for ListView Delphi XE7
(1 answer)
Closed 7 years ago.
Believe me I did my homework before reaching out for help. I spent last 3 days searching and reading but I couldn't come to a solution. So any help will be highly appreciated.
My task is to have a ListView connected to a Dataset where the ListView Item is of the following structure:
Bear in mind that
Elements 4, 6, & 8 are of fixed values & Color (i.e. labels)
Colors of Elements 1 & 10 depends on values of Elements 5, 7, & 9
Best what I got is references to Delphi Standard Example that shifts with Embarcadero Delphi Examples directory: ListViewMultiDetailAppearance.
This solution offers to create our own class for MutliDetailItemAppearance and register as many details as we need (in my case I need additional 8 I think).
Now my questions:
Is this the best approach?
if not, what is the better approach?
if it is, how adding additional 8 details will affect the
performance?
and most important how to reach custom coloring for elements for
each List View Item based on the values?
and finally how to reach this sections borders? and List item bottom
borders (the green line)?
Thank you very much for your thoughts in advance.
I am not sure that my way was correct, but I was using TListbox for alike purpose in my fmx project. The structure of its items was formed in the following way during filling from DataSource by LiveBindings.
procedure THMICD10Fr.LinkListControlToField1FillingListItem(Sender: TObject;
const AEditor: IBindListEditorItem);
begin
if (Assigned(AEditor)) and (HDM2.FDQicd_detail_for_TreeView.Active) then
try
if (AEditor.CurrentObject as TMetropolisUIListBoxItem).ChildrenCount = 2
then
begin
with TPanel.Create(AEditor.CurrentObject as TMetropolisUIListBoxItem) do
begin
Parent := (AEditor.CurrentObject as TMetropolisUIListBoxItem);
Align := TAlignLayout.alRight;
Width := 45;
Margins.Bottom := 1;
Margins.Top := 1;
end;
with TLabel.Create((AEditor.CurrentObject as TMetropolisUIListBoxItem)
.Children.Items[2] as TPanel) do
begin
Parent := (AEditor.CurrentObject as TMetropolisUIListBoxItem)
.Children.Items[2] as TPanel;
Text := '↓';
VertTextAlign := TTextAlign.taCenter;
TextAlign := TTextAlign.taCenter;
Align := TAlignLayout.alClient;
HitTest := true;
AutoSize := false;
StyledSettings := StyledSettings - [TStyledSetting.ssStyle];
Font.Style := Font.Style + [TFontStyle.fsBold];
Tag := HDM2.FDQicd_detail_for_TreeView.FieldByName('id').AsInteger;
TagString := HDM2.FDQicd_detail_for_TreeView.FieldByName
('category_etiology').AsString;
OnClick := LabelInListBox1Click;
end;
end;
except
end;
end;
This code gave me the following appearence:
You can create and nest all necessary TLayouts, TLabels etc. inside the Item and set all the necessary settings using the logics from inside the LiveBindings event handler.

Change gradient in ApplyStyleLookup

Im trying to change the gradient color of TSpeedbutton at runtime using ApplyStyleLookup, but for some reason only the top twothird of the button changes color. When I change it at design time I see three points for the gradient. I'm using the buttonstyle in the TSpeedbuttons StyleLookup. Using Delphi XE6 Rad Studio.
Thanks.
Added 8/29/14 I found the way to change the gradient see below. But my problem is on the buttonstyle have three rectangles and the one I need to access is the rectangle2 under background. What do I need to access it.
procedure TForm1.SpeedButton1ApplyStyleLookup(Sender: TObject);
var
BckObject: TFmxObject;
begin
BckObject := SpeedButton1.FindStyleResource('background');
if Assigned(BckObject) and (BckObject is TRectAngle) then
begin
TRectAngle(BckObject).Fill.Gradient.Style := TGradientStyle.Linear;
TRectAngle(BckObject).Fill.Gradient.Points.Points[0].Color := $FF0097A5;
TRectAngle(BckObject).Fill.Gradient.Points.Points[0].Offset := 0.25;
TRectAngle(BckObject).Fill.Gradient.Points.Points[1].Color := $FF0097F5;
TRectAngle(BckObject).Fill.Gradient.Points.Points[1].Offset := 1.00;
end;
end;
The rectangle2 (TRectAngle) under background has the StyleName property blank, by enter a name in the StyleName and save it I can access the rectangle2 by using the same method:
BckObject := SpeedButton1.FindStyleResource('myname');

Problem with adding graphics to TLabel

I'm trying to create with Delphi a component inherited from TLabel, with some custom graphics added to it on TLabel.Paint. I want the graphics to be on left side of text, so I overrode GetClientRect:
function TMyComponent.GetClientRect: TRect;
begin
result := inherited GetClientRect;
result.Left := 20;
end;
This solution has major problem I'd like to solve: It's not possible to click on the "graphics area" of the control, only label area. If the caption is empty string, it's not possible to select the component in designer by clicking it at all. Any ideas?
First excuse-me for my bad English.
I think it is not a good idea change the ClientRect of the component. This property is used for many internal methods and procedures so you can accidentally change the functionality/operation of that component.
I think that you can change the point to write the text (20 pixels in the DoDrawText procedure -for example-) and the component can respond on events in the graphic area.
procedure TGrlabel.DoDrawText(var Rect: TRect; Flags: Integer);
begin
Rect.Left := 20;
inherited;
end;
procedure TGrlabel.Paint;
begin
inherited;
Canvas.Brush.Color := clRed;
Canvas.Pen.Color := clRed;
Canvas.pen.Width := 3;
Canvas.MoveTo(5,5);
Canvas.LineTo(15,8);
end;
What methods/functionality are you getting from TLabel that you need this component to do?
Would you perhaps be better making a descendent of (say, TImage) and draw your text as part of it's paint method?
If it's really got to be a TLabel descendant (with all that this entails) then I think you'll be stuck with this design-time issue, as doesn't TLabel have this problem anyway when the caption is empty?
I'll be interested in the other answers you get! :-)

Resources