Jump to content
SillyQuestions

Issue with dynamic panel creation

Recommended Posts

Posted (edited)

I am having issue with panels created once scrolling is needed the width is too wide. I was having another issue with placement incorrect, but I fixed that issue already. 

 

There also another issue when resizing the form messes up to the position of panels.

unit Unit8;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.UITypes,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ToolWin, Vcl.ComCtrls,
  Vcl.ExtCtrls;

type
  TForm8 = class(TForm)
    ScrollBox1: TScrollBox;
    ToolBar1: TToolBar;
    Button3: TButton;
    Button4: TButton;
    procedure Button3Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form8: TForm8;

implementation

{$R *.dfm}

var
  ScrollBox: TScrollBox;

function GetInScrollBoxTopPos: integer;
var
  I: integer;
begin
  result := 0;
  for I := 0 to ScrollBox.ControlCount - 2 do
    if ScrollBox.Controls[I] is TPanel then
      result := result + TPanel(ScrollBox.Controls[I]).Height + 5;
end;

type
  TPanel160 = class
    panel: TPanel;
    constructor Create(AOwner: TComponent);
  end;

type
  TPanel40 = class
    panel: TPanel;
    constructor Create(AOwner: TComponent);
  end;

procedure TForm8.Button3Click(Sender: TObject);
var
 panel : TPanel160;
begin
   panel := TPanel160.Create(self)
end;

{ TPanel160 }

constructor TPanel160.Create(AOwner: TComponent);
begin
  panel := TPanel.Create(AOwner);
  panel.Parent := ScrollBox;
  panel.Color := TColorRec.Red;
  panel.Caption := '';
  panel.Height := 160;
  panel.Width := ScrollBox.Width - 20;
  panel.Top := GetInScrollBoxTopPos;
  panel.Left := 10;
  panel.Anchors := [akLeft, akRight];
end;

{ TPanel40 }

constructor TPanel40.Create(AOwner: TComponent);
begin
     panel := TPanel.Create(AOwner);
  panel.Parent := ScrollBox;
  panel.Color := TColorRec.Blue;
  panel.Caption := '';
  panel.Height := 40;
  panel.Width := ScrollBox.Width - 20;
  panel.Top := GetInScrollBoxTopPos;
  panel.Left := 10;
  panel.Anchors := [akLeft, akRight];
end;

procedure TForm8.Button4Click(Sender: TObject);
var
 panel : TPanel40;
begin
   panel := TPanel40.Create(self)
end;

procedure TForm8.FormCreate(Sender: TObject);
begin
    ScrollBox := ScrollBox1
end;

end.

 

copy.zip

Edited by SillyQuestions

Share this post


Link to post

Have you tried using scrollbox.clientwidth instead of scrollbox.width?

Share this post


Link to post
4 hours ago, PeterBelow said:

Have you tried using scrollbox.clientwidth instead of scrollbox.width?

Yes, I not really sure what wrong. The width part is minor issue the biggest issue is how resizing the form display things. 

Share this post


Link to post

It would help a lot if you'd explain the problem you're having.

 

Looking at the code, I'm _guessing_ that you only have a single vertical list of panels on that scrollbox.

 

However, I can't tell if you have horizontal scrolling active or not. Assuming you don't want the panels resized, then they should all have the same relative positioning.

 

I'm doing something similar with some panels on a scrollbox that does not do horizontal scrolling.

 

It put three across and unlimited rows.

 

I have a list of things and I go through the list and display them like this:

1 ---- 2 ---- 3

4 ---- 5 ---- 6

7 ---- 8 ---- 9

. . .

 

I have a row_count and col_count, and position them with a gutter above and to the left, and derive the Top and Left position of each one with simple math based on curr_row and curr_col that I track as I'm laying them down.

 

I save the Top, Left, Height, and Width in the objects in the list. The Height and Width are always constant, but the Top and Left change based on whether I want to show or hide them based on filtering.

 

I have no problem scrolling vertically. I don't want or need horizontal scrolling. And the form isn't sizeable. But there's no reason this approach shouldn't work. When the form size changes, check the dimensions and if the number per row changes, then hide then reset the panels on the scrollbox area to maximize the number you can have with the given gutter size you're using. (I'm using 20 for both, but you seem to be using 5 for Vertical and you're ignoring Horizontal.)

 

But if there was a problem based on this code, I'd say it's because you're calculating the positions by deriving them by SUMMING things up while scanning through your list. If you do allow horizontal scrolling, then that's just not going to work. The summing approach you're using will ONLY work properly if there's JUST ONE COLUMN. If there are multiple columns, you're going to get a steadily growing gap between rows in increments of the panel.Height+5. That might be happening simply because you have horizontal scrolling enabled. 

 

You need to use indices and calculate the TOP and LEFT based on a panel's index, the number of columns and rows you have, the gutter size between them, and current row and column.

Share this post


Link to post

Adjusting ScrollBox1.VertScrollBar.Range seems to fix issue. The other issue related to form resize is fix by just handling the on-size event.

Share this post


Link to post
Posted (edited)

EDIT: For it still has issue if I add many panels then resize form to larger size then lesser size and scroll up all panels are moving downwards and there is empty space at the top.

 

To try to be clearer I fill scrollbox with Tpanels, I expand window to full size scroll down then go back to normal window size, scroll up and nothing is above. The issue only happens if there enough Tpanels to cause scroll to be needed when window is expanded, and I scroll down before unexpanded.  The window doesn't need to be fully expanded either just enough to cause the bigger window to need to scroll and scroll down then go back to smaller size and try scrolling up which is just empty.

Edited by SillyQuestions

Share this post


Link to post

Try to not use anchors but set the new panel's Align to alTop instead. If you want vertical spacing set the panel's AlignWithMargin property to true and set its Margin.Bottom to the spacing you want and all other Margin members to 0.

Share this post


Link to post

Here's example of finger measure applied*.  The example refactored your code into a mypanel.  This code surfaces the numbers to aid in sizing.  *finger measure ~ 13 mm grid layout size.  I added a panel inside the ScrollBox

unit Unit8;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.UITypes,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ToolWin, Vcl.ComCtrls,
  Vcl.ExtCtrls;

type
  TmyPanel = class(TPanel)
  const
    // 96 dpi ~  25.4 mm * 4
    baseFinger = 96 div 2;
    baseFatFinger = 3 * 96 div 2;
  public
    TopOffset: Integer;
    Finger: Integer;
    class var UsedHeight: Integer;

    procedure Paint; override;
    procedure WMSIZE(var message: TWMSIZE);
    procedure SetUp(AParent: TWinControl; const AColor: TColor;
      const Offset: Integer; const AAlign: TAlign);
  end;

  TForm8 = class(TForm)
    ScrollBox1: TScrollBox;
    ToolBar1: TToolBar;
    Button3: TButton;
    Button4: TButton;
    Button1: TButton;
    Panel1: TPanel;
    procedure Button4Click(Sender: TObject);
  end;

var
  Form8: TForm8;

implementation

{$R *.dfm}

// Let's pass ScrollBox1 using ScrollBox in SetUp procedure parameter}
//var
//  ScrollBox: TScrollBox;

procedure TForm8.Button4Click(Sender: TObject);
var
 panel : TmyPanel;
 TagNu: NativeInt;
 Prnt: TWinControl;
 Aln: TAlign;
begin
  Prnt := panel1;//ScrollBox1;
  Aln := alTop;// alNone;
  panel := TmyPanel.Create(Self);
  TagNu := (Sender as TControl).Tag;
   Case Tagnu of
     0: panel.SetUp(Prnt,   clSkyBlue, 8, aln);
     1: panel.SetUp(Prnt,clMoneyGreen, 4, aln);
     2: panel.Setup(Prnt,     clCream, 2, aln);
   End;
end;

{ TmyPanel }
// Note how only current numbers are used and top offset used for naming not used for scrolling!
procedure TmyPanel.Paint;
begin
  inherited;
  canvas.TextOut(10,10,format('%s class Top %d Rect.Top %d',[Name, UsedHeight, BoundsRect.Top]));
end;

procedure TmyPanel.SetUp(AParent: TWinControl; const AColor: TColor; const Offset: Integer; const AAlign: TAlign);
begin
  Parent := AParent;
  self.Color := AColor;
  ParentBackground := False;
  // moved top to here to avoid firsttime flag
  // and when align = alNone sets panel top else
  // size the parent to fit with resize event
  finger := baseFinger;
  Top := UsedHeight + 2 * finger;
  Inc(UsedHeight, (Offset+1) * finger);
  TopOffset := UsedHeight;
  AParent.Height := TopOffset;
  Name := 'MyPanel'+'_'+ UsedHeight.ToString;
  Caption := '';//Name;// + ' ' + UseCount.ToString;
  Height := Offset * finger;

  Left := 2 * finger;
  Width := 200;//aParent.Width - 34 - Left;
  Align := AAlign;
  show;
//    if ScrollBox1.height > anOwner.Height div 2 then
//    begin
//      ScrollBox1.AutoSize := False;
//      ScrollBox1.Height := anOwner.Height div 2;
//    end;
//e


end;

procedure TmyPanel.WMSIZE(var message: TWMSIZE);

begin
  //Finger adjusted here
  finger := basefinger * {DPI/96} 1;
 end;

end.

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×