I am dynamically populating frames into flow panels (as part of a horizontally oriented VCL Metropolis app). I need to resize each group's flow panel to fit all its items horizontally. I have a very simple formula which does the trick sometimes, but not all the time - specifically when adding an odd number of items. The flow panel's FlowStyle
is set to fsTopBottomLeftRight
and fits 2 frames vertically.
For example, adding 7 items automatically detects the correct width (4 items across). But adding 5 items does not detect the correct width (supposed to be 3 across but winds up detecting 2 across).
How can I make it correctly calculate the width for each group?
Here's the procedure that populates the items into each item group (some irrelevant stuff removed):
procedure TSplitForm.LoadScreen;
const
FRAME_WIDTH = 170; //Width of each frame
FRAME_HEIGHT = 250; //Height of each frame
FRAME_MARGIN = 30; //Margin to right of each group
FRAME_VERT_COUNT = 2; //Number of frames vertically stacked
var
CurGroup: TFlowPanel; //Flow panel currently being populated
procedure ResizeGroup(FP: TFlowPanel);
var
Count, CountHalf, NewWidth, I: Integer;
begin
//Resize the specific flow panel's width to fit all items
Count:= FP.ComponentCount;
NewWidth:= FRAME_WIDTH + FRAME_MARGIN; //Default width if no items
if Count > 0 then begin
//THIS IS WHERE MY CALCULATIONS DO NOT WORK
CountHalf:= Round(Count / FRAME_VERT_COUNT);
NewWidth:= (CountHalf * FRAME_WIDTH) + FRAME_MARGIN;
end;
if FP.Parent.Width <> NewWidth then
FP.Parent.Width:= NewWidth;
//Resize main flow panel's width to fit all contained group panels
//(automatically extends within scroll box to extend scrollbar)
Count:= TFlowPanel(FP.Parent.Parent).ControlCount;
NewWidth:= 0;
for I := 0 to Count-1 do begin
NewWidth:= NewWidth + FP.Parent.Parent.Controls[I].Width;
end;
NewWidth:= NewWidth + FRAME_MARGIN;
if FP.Parent.Parent.Width <> NewWidth then
FP.Parent.Parent.Width:= NewWidth;
end;
procedure Add(const Name, Title, Subtitle: String);
var
Frame: TfrmItemFrame;
begin
Frame:= AddItemFrame(CurGroup, Name); //Create panel, set parent and name
Frame.OnClick:= ItemClick;
Frame.Title:= Title;
Frame.Subtitle:= Subtitle;
ResizeGroup(CurGroup);
end;
begin
CurGroup:= fpMainGroup;
Add('boxMainItem1', 'Item 1', 'This is item 1');
Add('boxMainItem2', 'Item 2', 'This is item 2');
Add('boxMainItem3', 'Item 3', 'This is item 3');
Add('boxMainItem4', 'Item 4', 'This is item 4');
Add('boxMainItem5', 'Item 5', 'This is item 5');
CurGroup:= fpInventoryGroup;
Add('boxInventItem1', 'Item 1', 'This is item 1');
Add('boxInventItem2', 'Item 2', 'This is item 2');
Add('boxInventItem3', 'Item 3', 'This is item 3');
Add('boxInventItem4', 'Item 4', 'This is item 4');
Add('boxInventItem5', 'Item 5', 'This is item 5');
Add('boxInventItem6', 'Item 6', 'This is item 6');
Add('boxInventItem7', 'Item 7', 'This is item 7');
end;
This is a screenshot of what that code is producing:
As you can see, the first group with 5 items is hiding the 5th item, but the second group with 7 items is showing all 7 just fine.
The structure of parent/child relationships is like so (with flow panels in question bold):
I tried using each flow panel's AutoSize
property, but it didn't acknowledge the heights (2 up) and made things even worse. I basically just need to properly detect the total number of columns within these flow panels.
Round(Count / FRAME_VERT_COUNT);
Here FRAME_VERT_COUNT
is 2
. When Count
is 5
your expression becomes
Rount(2.5);
The default rounding mode is bankers rounding and this evaluates to 2
. When Count
is 7
the expression is
Round(3.5);
Bankers rounding means this is 4
.
You could do what Sertac suggests and use ceil
. However, I would simply avoid floating point altogether. It is just not needed and as a general rule, integer arithmetic is always to be preferred if it is viable. Your expression should be
(Count + FRAME_VERT_COUNT - 1) div FRAME_VERT_COUNT