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.
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.
As shown like here.
pic: tabs with memo
Currently, my TMEMO displays bunch of different data, like this:
Data #1
Paragraphs
Data #2
Paragraphs
Data #N
Paragraphs
So to avoid scrolling, I want to add tabs to the Nth number.
So what components do I need and how should I intiate the process?
you need to use a combination of a TMemo and TTabControl.
Do not know how you get your paragraphs but you'll have to iterate through them, creating a TabSheet and a Memo for each.
procedure TfrmMemo.CreateTabsWithMemo;
var
pgControl: TPageControl;
TabSheet: TTabSheet;
Memo: TMemo;
begin
pgControl := TPageControl.Create(self);
pgControl.Parent := Self;
pgControl.Align := alClient;
//Do this for each paragraph
TabSheet := TTabSheet.Create(pgControl);
TabSheet.PageControl := pgControl;
TabSheet.Caption := Format('Tab %d', [pgControl.PageCount]);
Memo := TMemo.Create(TabSheet);
Memo.Parent := TabSheet;
Memo.Align := alClient;
Memo.Lines.Text := 'Your Paragraph here'
///
end;
Use TPageControl and TTabSheet. Place a TMemo component on each TTabSheet.
You can drage the TPageControl onto the form to get started.
We have a win control object which moves its clients to some other coordiantes. The problem is, when there are too many children - for example 500 controls - the code is really slow.
It must be because of each control being repainted each time I set Left and Top property. So, I want to tell the WinControl object stop being repainted, and after moving all objects to their new positions, it may be painted again (Something like BeginUpdate for memo and list objects). How can I do this?
Here's the code of moving the objects; it's quite simple:
for I := 0 to Length(Objects) - 1 do begin
with Objects[I].Client do begin
Left := Left + DX;
Top := Top + DY;
end;
end;
As Cosmin Prund explains, the cause for the long duration is not an effect of repainting but of VCL's realignment requisites at control movement. (If it really should take as long as it does, then you might even need to request immediate repaints).
To temporarily prevent realignment and all checks and work for anchors, align settings and Z-order, use DisableAlign and EnableAlign. And halve the count of calls to SetBounds by called it directly:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
Control: TControl;
begin
for I := 0 to 499 do
begin
Control := TButton.Create(Self);
Control.SetBounds((I mod 10) * 40, (I div 10) * 20, 40, 20);
Control.Parent := Panel1;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
C: TControl;
begin
// Disable Panel1 paint
SendMessage(Panel1.Handle, WM_SETREDRAW, Integer(False), 0);
Panel1.DisableAlign;
try
for I := 0 to Panel1.ControlCount - 1 do
begin
C := Panel1.Controls[I];
C.SetBounds(C.Left + 10, C.Top + 5, C.Width, C.Height);
end;
finally
Panel1.EnableAlign;
// Enable Panel1 paint
SendMessage(Panel1.Handle, WM_SETREDRAW, Integer(True), 0);
// Update client area
RedrawWindow(Panel1.Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
end;
end;
Your assumption that the slowness comes from re-painting controls is probably true, but not the whole story. The default Delphi code that handles moving controls would delay painting until the next WM_PAINT message is received, and that would happen when the message queue is pumped, after you complete moving all the controls. Unfortunately there are a lot of things involved in this, that default behavior can be altered in many places, including Delphi and Windows itself. I've used the following code to test what happens when you move a control at runtime:
var i: Integer;
begin
for i:=1 to 100 do
begin
Panel1.Left := Panel1.Left + 1;
Sleep(10); // Simulate slow code.
end;
end;
The behaviour depends on the control! A TControl (example: TLabel) is going to behave according to Delphi's rules, but a TWinControl depends on too many factors. A simple TPanel is not repainted until after the loop, in the case of TButton on my machine only the background is re-painted, while a TCheckBox is fully repainted. On David's machine the TButton is also fully repainted, proving this depends on many factors. In the case of TButton the most likely factor is the Windows version: I tested on Windows 8, David tested on Windows 7.
AlignControl Avalanche
Anyhow, there's an other really important factor to be taken into account. When you move a control at runtime, all the rules for alignment and anchoring for all the controls need to be taken into account. This likely causes an avalanche of AlignControls / AlignControl / UpdateAnchorRules calls. Since all those calls end up requiring recursive invocations of the same, the number of calls will be exponential (hence your observation that moving lots of objects on a TWinControl is slow).
The simplest solution is, as David suggests, placing everything on a Panel and moving the panel as one. If that's not possible, and all your controls are actually TWinControl (ie: they have a Window Handle), you could use:
BeginDeferWindowPos, DeferWindowPos, EndDeferWindowPos
I would put all the controls in a panel, and then move the panel rather than the controls. That way you perform the shift in a one single operation.
If you would rather move the controls within their container then you can use TWinControl.ScrollBy.
For what it is worth, it is more efficient to use SetBounds than to modify Left and Top in separate lines of code.
SetBounds(Left+DX, Top+DY, Width, Height);
To speed up you should set the Visible property of you WinControl to False during child movement to avoid repainting.
Together with SetBounds you will get the best from moving the child controls.
procedure TForm1.MoveControls( AWinControl : TWinControl; ADX, ADY : Integer );
var
LIdx : Integer;
begin
AWinControl.Visible := False;
try
for LIdx := 0 to Pred( AWinControl.ControlCount ) do
with AWinControl.Controls[LIdx] do
begin
SetBounds( Left + ADX, Top + ADY, Width, Height );
end;
finally
AWinControl.Visible := True;
end;
end;
BTW As David suggested, moving the parent is much faster than each child.
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! :-)