I want to change the caption of a panel when it is out of view when scrolling in a TScrollBox. I have a scrollbox where all the all the categories are listed under each other and as the title of each category scrolls past i want the top panel to change to show which category I am currently scrolling through. How exactly can i do this?
Asked
Active
Viewed 814 times
2
-
I have this feeling that you are asking the wrong question for what you need to do. So, please edit your question and provide some background for why you need to change the name in the first place, and why it has to be done when the panel is out of view. – Tom Brunberg Jun 21 '20 at 18:15
-
@TomBrunberg is right. Changing the name of a control based on its current visibility in a scroll box sounds *very* strange. Most likely there is a better way to solve your actual problem. Nevertheless, it is very much doable to respond to when a control becomes visible/invisible due to scrolling, as described in my A. – Andreas Rejbrand Jun 21 '20 at 18:18
-
So you want to change the *caption* of the panel, not the *name*? – Andreas Rejbrand Jun 21 '20 at 18:24
-
@AndreasRejbrand yes sorry. – okok Jun 21 '20 at 18:25
-
The panel whose caption you want to change, is that panel really a child of the scroll box? Because if so, this seems like a rather dull Q, because when the panel isn't visible, it doesn't matter what caption it has (because you won't see it!). Perhaps you mean that you want to change the caption of some panel outside the scroll box as you scroll the scroll box? – Andreas Rejbrand Jun 21 '20 at 18:29
-
yes sorry I seem to have the dumb. But yes I want to change the caption of a Panel outside of the scrollbox. – okok Jun 21 '20 at 18:33
1 Answers
4
To see if any pixel of a child control ChildCtrl
currently is visible in the parent TScrollBox
control named ScrollBox
, check
ScrollBox.ClientRect.IntersectsWith(ChildCtrl.BoundsRect)
This is just one definition of "not out of view", however. If you instead want to check if the entire control is visible, instead check
ScrollBox.ClientRect.Contains(ChildCtrl.BoundsRect)
To detect scrolling, you would love a published OnScroll
property of TScrollBox
, but unfortunately there is no such property. Instead, you must intercept the scroll messages yourself, as detailed in this Q&A.
Here is a complete example (just quick and dirty to show how it is done -- in a real app, you would refactor it):
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, StdCtrls;
type
TScrollBox = class(Vcl.Forms.TScrollBox)
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
end;
TForm1 = class(TForm)
ScrollBox1: TScrollBox;
lblTitle: TLabel;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FButtons: TArray<TButton>;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
const
N = 30;
var
i, y: Integer;
btn: TButton;
begin
// First, populate the scroll box with some sample buttons
SetLength(FButtons, N);
y := 10;
for i := 0 to N - 1 do
begin
btn := TButton.Create(ScrollBox1);
btn.Parent := ScrollBox1;
btn.Left := 10;
btn.Top := y;
btn.Caption := 'Button ' + (i + 1).ToString;
Inc(y, 3*btn.Height div 2);
FButtons[i] := btn;
end;
end;
{ TScrollBox }
procedure TScrollBox.WMVScroll(var Message: TWMVScroll);
var
i: Integer;
begin
inherited;
for i := 0 to High(Form1.FButtons) do
if Form1.ScrollBox1.ClientRect.Contains(Form1.FButtons[i].BoundsRect) then
begin
Form1.lblTitle.Caption := Form1.FButtons[i].Caption;
Break;
end;
end;
end.
Don't forget to set TScrollBox.VertScrollBar.Tracking
to True
!

Andreas Rejbrand
- 105,602
- 8
- 282
- 384
-
I have added the system.types unit and still can't find the IntersectsWith command. – okok Jun 21 '20 at 18:30
-
It might not have been introduced in D2010 yet. I don't know exactly when it was introduced. If it is not present, you can always use plain inequalities: `(C.Left < SB.Right) and (C.Right > SB.Left) and (C.Top < SB.Bottom) and (C.Bottom > SB.Top)` where `SB := ScrollBox.ClientRect` and `C := ChildCtrl.BoundsRect` for the first case. – Andreas Rejbrand Jun 21 '20 at 18:36