1
votes

I noticed in one of my test applications after I have added so many TPanels to a TScrollBox I run into a problem with the ones drawn past that certain amount. I disable the scroll box prior to drawing and it is always cleared before drawing so there are no relative position problems. Initially I thought maybe I had run into some sort of maximum height to paint. So you know the arrangement is width occupying panels stacked vertically.

So I created a new project to try and identify and tackle the problem and it has revealed an additional detail to the problem. When I'm home I can provide the example and a video but I will describe for now. Made a form with a TScrollBox a spinedit to specify how many panels a button to create the panels in a loop and a button to free the panels and empty the array for another try. I set the caption to the number in the loop for identification.

I have tried 2 ways of stacking to see if bit mattered. One is to set the position I times height so if height is 200 then i * 202 giving it a space of 2px. The new way I tried was using align top instead. It might have a slight difference in effect but the problem in general is still the same.

The new problem is with the scroll range. After the loop is done and the scroll box is enabled scrolling down to the bottom stops at the last numbered panel. But its out of place maybe 199 right below 169 its 200 panels. Then the scroll bar adjusts range letting me reach the bottom only to see the next to last panel 198. I believe this occurs using the align top method as it never occurred in my app. I will test further.

Bottom panel not placing aside I think the solution to that problem is to manually calculate and set the range.

The main problem which occurs in my app when setting the position instead of using align top is that after a certain number of panels they all position at the end in the same spot. Before a certain amount its fine say 50 or 100 but after so many it happens. I know 200 times 200 is a pretty small integer but maybe there is a address limit?

I will continue to test I still need to check if panel height adds into it. But figured this has to be a known issue. Delphi 2009 by the way.

1
Please submit bug reports to Quality Central. That's also the place to search for known bugs.Rob Kennedy

1 Answers

7
votes

It's a Windows limitation: the size of a Windowed control cannot exceed 65,535 pixels.

See the documentation on the WM_SIZE message wherein width and height are passed together in a single 32 bit parameter:

lParam

The low-order word of lParam specifies the new width of the client area.

The high-order word of lParam specifies the new height of the client area.

So width and height values are limited to 16 bits. That is: when SetWindowPos or alike is involved, which is called by SetBounds, which is called by setting Top.

Subsequently, the Top value of a control, which can be negative, is limited to 15 bits and 1 sign bit, thus +-32,767. To be specific: that is where Control.ClientOrigin.X/Y is bound by. E.g. thus resolving in a maximum Top value of 32,167 for a control placed in the middle of a 1920x1200 pixel screen.

So this is why the last panels appear on the same spot within your scroll box.

Note that this limitation does not apply to VCL controls without Windows handle.


How to solve?

The Top property of a child control within a scroll box is relative to the box's visible clientrect; scrolling the scroll bar resets the Top properties of all children.

So (just) before reaching the magic limit, fool Windows by scrolling the scroll box:

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  J: Integer;
  P: TPanel;
begin
  ScrollBox1.DisableAlign;
  try
    ScrollBox1.VertScrollBar.Range := 400 * 202; // 80,800 ! ;-)
    for I := 0 to 3 do
    begin
      ScrollBox1.VertScrollBar.Position := I * 100 * 202;
      for J := 0 to 99 do
      begin
        P := TPanel.Create(Self);
        P.SetBounds(0, J * 202, 100, 200);
        P.Align := alCustom;
        P.Caption := IntToStr(I * 100 + J);
        P.ParentBackground := False;
        P.Color := Random(clWhite);
        P.Parent := ScrollBox1;
      end;
    end;
  finally
    ScrollBox1.VertScrollBar.Position := 0;
    ScrollBox1.EnableAlign;
  end;
end;

How to solve it better?

Instead of TPanel (a TWinControl), use TControl derivatives to bypass API calls to SetWindowPos.


How to solve it best?

Use a virtual approach, like TDBControlGrid does, showing only a few panels while giving the impression of having a lot.