TVirtualStringTree ScaleBy Stretch - delphi

I'm Trying to get a VST to resize automatically when its Height and Width is changed.
I don't have this problem with other placed VCL components, some of them have a Property "Stretch" like TImage, which lets them adjust automatically. The VST remains stubborn and keeps nodeHeights and Column widths.
I have seen Header->hoAutoResize, hoColumnResize. In this matter, AutoSizeIndex <> -1 is not so useful, since I need every column to scale down. I think that's why these don't do anything. Changing the AutoSizeIndex to my last Column (3, because i have 4 Columns) and having hoAutoResize = True; still doesn't affect my column widths. hoColumnResize is the setting that lets the User resize columns, so no luck with that either
I have seen TreeOptions->AutoOptions->toAutoChangeScale and toAutoSpanColumns. I found out that toAutoSpanColumns is counter-productive in my case, so that's off. My Font-Size is adjusting.
I have found Tree.Scaleby, but I can't make it work in my favor and it's undocumented in the official .pdf docs I have.
All 4 columns have minWidth of 10, so no issue there
All 4 have coEnabled, coVisible, coResizable := True and coFixed, coSmartResize := False fwiw
I guess I'm just hitting the wrong combinations of settings or something.
Any hints would be great, thanks.

From your post I understand that you want to automatically adjust widths of all columns proportionally when the TVirtualStringTree width changes. If you want that to also happen with the row heights, you can just apply the following correspondingly.
There is no setting for the a propertional column width, but it is simple to achieve in the OnResize event of the TVirtualStringTree:
procedure TForm1.VSTResize(Sender: TObject);
begin
VST.Header.Columns[0].Width := MulDiv(VST.Width, 50, 100);
VST.Header.Columns[1].Width := MulDiv(VST.Width, 30, 100);
VST.Header.Columns[2].Width := MulDiv(VST.Width, 20, 100);
end;
In the above, the columns are kept as 50%, 30% and 20% of the components width.

You need to set Header->AutoSizeIndex to the index of yours last column, besides the Header->hoAutoResize to true.
Something like this if you wish to do it programmatically:
vt.Header.Options := vt.Header.Options + [hoAutoResize];
vt.Header.AutoSizeIndex := vt.Header.Columns.GetLastVisibleColumn;

Related

How to calculate the height of a TCategoryButtons at run-time?

In a Delphi 10.4.2 VCL Application in Windows 10, how can I calculate the height of a TCategoryButtons object at run-time, i.e. the sum of all its button heights and its Category items, as this height could vary depending on its font size?
Measuring the pixel heights at run-time, I have noticed that all buttons have the same height and that the buttons have a different height as the Category items.
Also, note that the buttons do not have a published Height property in the Object Inspector.
But shouldn't it be possible to calculate the sum of all its button heights and its Category items with some low-level methods?
This is a control entirely implemented in Pascal, in Vcl.CategoryButtons.pas.
Therefore, you can see exactly how it is implemented. For instance, in TCategoryButtons.Paint you see its complete drawing code. Similarly, you can investigate the hit testing done in MouseMove (or MouseDown or MouseUp).
Consequently, if nothing else, you can create your own modified version of TCategoryButtons using this code. Your version can save the total height when it has been determined (for instance, certainly after painting).
However, after a quick look, it seems like TButtonCategory.Bounds might be interesting. If you are lucky, this returns the on-screen rect of a category. The Bottom of the last category's rect should be the (effectively used) height of the entire control.
It seems to work for me:
Here I draw a red bar of the same height as the control.
procedure TForm5.FormPaint(Sender: TObject);
begin
var y := CategoryButtons1.Categories[
CategoryButtons1.Categories.Count - 1
].Bounds.Bottom;
Canvas.Brush.Color := clRed;
Canvas.FillRect(Rect(0, 0, ClientWidth, y))
end;

Delphi TChart Bar Width

I'm trying to figure it out how to set a fixed size to the bars' width in TChart.
I've tried to search a lot for a similar "problem", but I may be using the wrong terms, because I found no related topics in the internet.
My chart has Horizontal Scrolling enabled, so I would like to have bars with the same width regardless how many bars are in the chart.
Here's a sample where I added 10 sample values and, for my purpose, this bar width is acceptable:
But when I add 40 sample values, for example, the bars became so thin, even when showing only 10 values and letting all the others visible only after scroll the bars to the left:
Here's the code I'm using to add the sample values (just for you to see there's nothing special):
procedure TForm5.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i := 0 to ChartFatGrupoPerc.SeriesCount-1 do
ChartFatGrupoPerc.Series[i].FillSampleValues(StrToInt(eItems.Text));
ChartFatGrupoPerc.BottomAxis.Maximum := StrToInt(eMax.Text);
ChartFatGrupoPerc.BottomAxis.Minimum := StrToInt(eMin.Text);
end;
So, the question is: Is there an way I can define the bars' width regardless the amount of values?
Thanks in advance,
Kelver Merlotti [KM]
Is there an way I can define the bars' width regardless the amount of values?
Yes, setting these properties would do the trick:
AutoBarSize := false;
CustomBarWidth := aSuitableNumber;

TeeChart VCL chart scale issue

I have an issue using Delphi 2007 & TChart 7.0.10.0 or 7.0.11.0 or the latest evaluation 9.0.5.0 on the TChart scaling.
The problem arises as soon I enlarge the window after a certain width and KEEP the Form height!
This is the drawing using a smaller form size.
now if I enlarge to 1200 weight I get this ugly scaling:
If I export in the designer without the aspect ratio set and with 1200 weight you will se this:
How to get ride of this?
Hp
I see you've set top and bottom margins to Chart1 in your project (8 and 20 percent respectively). I guess this has the intention of giving more space (in height) for Chart2 when you resize the form making it bigger.
Chart1's Top and Height properties should be set according to fill this blank space in the Form's OnResize event.
Try this:
procedure TGSSkillgroupStatisticForm.FormResize(Sender: TObject);
begin
Chart1.Draw;
Chart2.Top:=Chart1.ChartRect.Bottom + 25;
Chart2.Height:=Chart1.Height-Chart1.ChartRect.Bottom-40;
end;
Steema Support Central
Keep in mind that I only scale in the x-axis. Your 3-D bar / construct will after a certain width, overlap the scaling numbers! Your given answer do not fix this issue at all. To see the real problem in a better way, I added on the form creation:
Chart2.BottomAxis.Maximum := 20;
Series2.AddBar(12, 'Hallo', clred);
Here the result:

How do I determine the height of a line of text in a TMemo programmatically?

I've got a TMemo, and I want to always make it exactly high enough to display the number of lines it contains. Unfortunately, I don't quite know how to calculate that. I can't base it off the .Font.Size property, because that will vary based on DPI. And I can't use TCanvas.TextHeight because TMemo doesn't seem to have a canvas.
Anyone know how to do this right?
I see a problem, i think all lines on a TMemo are equal on height, but some can be empty...
So getting Height of empty ones will give zero while they are not zero height on the TMemo.
So the solution maybe doing something like Memo.Lines.Count*LineHeight
Beware that the Lineheight may not be getted by Canvas.TextHeight since Canvas.TextHeight will give more or less exact height of minimal height for a text... i mean it will not give same height for text 'ABC' than for 'ABCp', etc...
I would recomend (if not want to call Windows API) to use Font.Height, but if it is negative the internal leading of each line is not measured...
So i would recomend to do the next steps (tested):
Asign a positive value for Memo.Font.Height on the OnCreate event or anywhere you want, with this the lineheight ot the TMemo will be such value you asigned
Total height now can be obtained directly by Memo.Lines.Count*LineHeight, since you have asigned a positive value to Memo.Font.Height (remember that would make Memo.Font.Size to be negative)
Personally i do this on the TForm OnCreate event (to ensure it is done only once), just to ensure visual font size is not changed and MyMemo.Font.Height includes internal leading of each line:
MyMemo.Font.Height:=Abs(MyMemo.Font.Size*MyMemo.Font.PixelsPerInch div Screen.PixelsPerInch);
Ensure the previous to be done only once, otherwise the text size will be visaully bigger and bigger, as much as times you run it... it is caused because not allways MyMemo.Font.PixelsPerInch is equal to Screen.PixelsPerInch... in my case they are 80 and 96 respectively.
Then, when i need to know line height i just use:
Abs(MyMemo.Font.Height*Screen.PixelsPerInch div 72)
That gives exact height of one TMemo line, since all lines have the same height, the total height would be:
MyMemo.Lines.Count*Abs(MyMemo.Font.Height*Screen.PixelsPerInch div 72)
So, to make TMemo height as big as its contained text i do this (read the comment of each line, they are very important):
MyMemo.Font.Height:=Abs(MyMemo.Font.Size*MyMemo.Font.PixelsPerInch div Screen.PixelsPerInch); // I do this on the Tform OnCreate event, to ensure only done once
MyMemo.Height:=1+MyMemo.Lines.Count*Abs(MyMemo.Font.Height*Screen.PixelsPerInch div 72); // I do this anywhere after adding the text and/or after editing it
I you do not want to play with Screen.PixelsPerInch you can just do this (read the comment of each line, they are very important):
MyMemo.Font.Height:=Abs(MyMemo.Font.Height); // This may make text size to visually change, that was why i use the corrector by using MyMemo.Font.PixelsPerInch and Screen.PixelsPerInch
MyMemo.Height:=1+MyMemo.Lines.Count*Abs(MyMemo.Font.Height*MyMemo.Font.PixelsPerInch div 72);
Hope this can help anyone.
You can write your own implementation of TCanvas.TextHeight for TMemo:
function CountMemoLineHeights(Memo: TMemo): Integer;
var
DC: HDC;
SaveFont: HFont;
Size: TSize;
I: Integer;
begin
DC:= GetDC(Memo.Handle);
SaveFont:= SelectObject(DC, Memo.Font.Handle);
Size.cX := 0;
Size.cY := 0;
// I have not noticed difference in actual line heights for TMemo,
// so the next line should work OK
Windows.GetTextExtentPoint32(DC, 'W', 1, Size);
// BTW next (commented) line returns Size.cY = 0 for empty line (Memo.Lines[I] = '')
// Windows.GetTextExtentPoint32(DC, Memo.Lines[I], Length(Memo.Lines[I]), Size);
Result:= Memo.Lines.Count * Size.cY;
SelectObject(DC, SaveFont);
ReleaseDC(Memo.Handle, DC);
end;
You need to use a TCanvas for this. You can either create a TBitMap in the background and use its TCanvas (after assigning the Memo's Font property to the Bitmap.Canvas' one), or use a TCanvas from another component. Somthing like this:
BMP:=TBitMap.Create;
TRY
BMP.Canvas.Font.Assign(Memo.Font);
TotalHeight:=0;
FOR LineNo:=1 TO Memo.Lines.Count DO INC(TotalHeight,BMP.Canvas.TextHeight(Memo.Lines[PRED(I)]))
FINALLY
FreeAndNIL(BMP)
END;
Edit:
Or perhaps this one:
BMP:=TBitMap.Create;
TRY
BMP.Canvas.Font.Assign(Memo.Font);
LineHeight:=BMP.Canvas.TextHeight('Wq');
TotalHeight:=Memo.Lines.Count*LineHeight
FINALLY
FreeAndNIL(BMP)
END;
I originally suggested looing at the "Lines" TStrings list member in TMemo.
Instead, please look at the "Font" member in the parent class, TCustomEdit.
'Hope that helps .. PS

How to find the real size ("logical area") of a TScrollBox

I need to find the entire size (also called "logical area") of a TScrollBox - as opposite to visible area that you get via Width and Height property (or ClientWidth ClientHeight).
I want to create some controls inside that TScrollBox. One of them (called TViewer) needs to be as high as the TScrollBox itself. The thing is that during creation, the TScrollBox is scrolled down to show last created control. So, using Top=1 will not work because my control will have top=1 which is not the top of the logical area.
Delphi 7
Drop a component, like a TLabel, on the TScrollBox.
Set the component's Left and Top properties to 0.
Set the component's Visible property to False.
Now you always have the origin. The "logical height" is now:
myScrollBox.Height + (myOriginControl.Top * -1);
Maybe ScrollBox.HorzScrollBar.Range and ScrollBox.VertScrollBar.Range + the corresponding .Positions are what you need.
Look at Scrollbars:
ScrollBox1.VertScrollBar.Range
ScrollBox1.HorzScrollBar.Range
It can be less than height and width if the scrollbox logical area is not bigger than phisical area (scrollbars not visible in that case)
Or use this to get the max of both:
var
AHeight, AWidth: Integer;
begin
AHeight := Max(ScrollBox1.VertScrollBar.Range, ScrollBox1.Height);
AWidth := Max(ScrollBox1.HorzScrollBar.Range, ScrollBox1.Width);
ShowMessageFmt('%d,%d', [AHeight, AWidth]);
end;
Edit
From #Altar comments, I can add the logical height and/or width is not the problem. If you want to add any control which occupies all the height of the scrollbar, use the AHeight from the above calculation, but set the Top to the negative of VertScrollBar.Position, something like this:
procedure TForm2.Button3Click(Sender: TObject);
var
AHeight, AWidth: Integer;
Btn: TButton;
begin
AHeight := Max(ScrollBox1.VertScrollBar.Range, Height);
AWidth := Max(ScrollBox1.HorzScrollBar.Range, Width);
Btn := TButton.Create(Self);
Btn.Parent := ScrollBox1;
Btn.Left := 15;
Btn.Top := -ScrollBox1.VertScrollBar.Position;
Btn.Height := AHeight;
end;
Im not sure i understand exactly what you want to do, but to get the complete area defined as "scrollable" you would have to write ScrollBox.HorScrollBar.Range + ScrollBox.Clientwidth (and the same thing for the vertical section). The scrollbox always deducts the visible "page" size from the total. So if you define a height of 1000 pixels, with 100 pixels showing - it will have a scrolling range of 900. You have to add the clientheight to get the rest.
Also, to get the correct "top" position you will have to read Canvas.Cliprect.Top, since a scrolling window does not change the top position of sub-controls. Windows handles this for you and only tells you what to re-draw once the scrollbars are initialized.
Since you want to create a control that is just as high as the complete scrollable area, i presume you are creating an editor of sorts?
If so you would probably get better results taking a look at SynEdit and extract the base-class that adds scrollbars to a normal TCustomControl (it's quite easy). That way you can control both the painting and the layout of your controls.
Here is one I wrote for Lazarus and FreePascal a while back. If you add messages to the uses clause and prefix the message handlers with WM rather than TLM it will compile under Delphi.
(code to long, had to use external link): http://delphimax.wordpress.com/2010/09/20/platform-independent-image-component-for-lazarus/
I have tried to do that, and believe me, I was not able to.
If you have the instances of the controls that are inside TScrollBox, you can use them to calculate (not precisely) the area.
Here is a complicated (but complete) solution:
I create my first child control during TScrollBox.Create (when TScrollBox does not have yet a scrollbar)
Set Child.Top:= 1
Create the rest of the child controls (this may force the TScrollBox to show the scrollbar)
Scroll the TScrollBox up
Use one of the above solutions to calculate the height of the TScrollBox

Resources