如何使标签在滚动框中平滑居中?
How to keep label smoothly centered in scrollbox?
我在 TScrollBox
中使用 TMemo
来显示一些文本,并在顶部使用 TLabel
作为 header 信息。有时备忘录比滚动框宽,当然 Horizontal scroll bar
可以用来左右滚动以查看备忘录中的文本。
我想要一个标签作为 header 始终以滚动框可见区域为中心。我可以通过设置 Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2);
来做到这一点并且它可以工作但是它有点闪烁,来回滚动时摇晃。备忘录移动流畅,标签移动不流畅。
这里是单位:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TScrollBox=Class(VCL.Forms.TScrollBox)
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
private
FOnScrollHorz: TNotifyEvent;
public
Property OnScrollHorz:TNotifyEvent read FOnScrollHorz Write FonScrollHorz;
End;
TForm1 = class(TForm)
ScrollBox1: TScrollBox;
Label1: TLabel;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure ScrollBox1Resize(Sender: TObject);
private
procedure MyScrollHorz(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TScrollBox.WMHScroll(var Message: TWMHScroll);
begin
inherited;
if Assigned(FOnScrollHorz) then FOnScrollHorz(Self);
end;
procedure TForm1.MyScrollHorz(Sender: TObject);
begin
Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2);
end;
procedure TForm1.ScrollBox1Resize(Sender: TObject);
begin
Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ScrollBox1.OnScrollHorz := MyScrollHorz;
end;
end.
和 dfm:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 212
ClientWidth = 458
Color = clBtnFace
DoubleBuffered = True
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object ScrollBox1: TScrollBox
Left = 0
Top = 0
Width = 458
Height = 212
HorzScrollBar.Smooth = True
HorzScrollBar.Tracking = True
Align = alClient
BiDiMode = bdLeftToRight
DoubleBuffered = True
ParentBiDiMode = False
ParentDoubleBuffered = False
TabOrder = 0
OnResize = ScrollBox1Resize
ExplicitHeight = 337
object Label1: TLabel
Left = 192
Top = 30
Width = 69
Height = 13
BiDiMode = bdLeftToRight
Caption = 'Details header'
ParentBiDiMode = False
end
object Memo1: TMemo
Left = 24
Top = 70
Width = 700
Height = 89
Lines.Strings = (
'Details...')
TabOrder = 0
end
end
end
我尝试使用 DoubleBuffered
但没有帮助。
有什么建议可以让 Label1 在没有 flickering/shaking 的情况下像滚动时的 Memo1 一样流畅吗?
编辑:
设计最终将是我在表单上有 3 个或滚动框,每个滚动框最多包含 3 个 header 的备忘录。滚动需要通过滚动框进行,因为同一滚动框中的所有备忘录都需要同时滚动。这意味着我看不出将标签放在表单或面板上然后放在滚动框外部的表单上如何工作:
编辑 2:
下面的答案确实提供了很好的解决方案,但它们确实需要将居中的 Labels
放置在 Scrollbox
之外并放在 Form
本身上。然后直接在 Form
上移动 Scrollbox's
scroll bars
或 scroll bars
。这确实得到了预期的效果,但它增加了一些不便,因为 Labels
不再是 Scrollbox
的一部分。
-" 备忘录移动流畅,标签不流畅。"
那是因为你试图阻止它移动。分离您的 OnScrollHorz
处理程序,标签将顺利移动。但这不是你想要的,它将不再以表格为中心。
问题是,在 inherited
调用 (WM_HSCROLL
) 期间,标签会随着备忘录一起移动。默认处理后,您重新定位标签,因此出现闪烁。
您可以公开一个额外的事件处理程序,它将在默认滚动 (OnBeforeHorzScroll) 之前触发,并在触发时隐藏标签。虽然 平滑地居中 ,但它会导致标签暂时消失的另一种闪烁。可能还是不尽如人意。
解决方案是使用窗体的父级控件,即滚动框的同级控件。你不能用 TLabel
来做,因为它是一个图形控件,但你可以使用 TStaticText
。如果 static 在设计时不小心放在滚动框后面,IDE 的 "structure pane" 可能会派上用场。
你可以这样做:
在您的表单上放置一个 ScrollBar,而不是 ScrollBox。将其对齐方式设置为底部(如果您希望有更多列或者您可以将每一列放在自己的面板中,则手动设置其大小和位置)。然后设置备忘录的大小并将标签放在表格的中心。设置备忘录的大小后(可能通过代码动态设置)放置此代码:
ScrollBar1.Min:=0-Memo1.Left;
ScrollBar1.Max:=Memo1.Width-Form1.ClientWidth+Memo1.Left;
最后就是设置ScrollBar的OnChange事件:
procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
Memo1.Left:=0-ScrollBar1.Position;
Memo2.Left:=0-ScrollBar1.Position;
...
MemoXY.Left:=0-ScrollBar1.Position;
end;
您的表单应如下所示:
完成!你有一个稳定的居中标签和平滑滚动的备忘录。
编辑:
这是一个版本,每个面板有 3 列,还带有垂直滚动条:
以及整个源代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Memo1: TMemo;
Memo2: TMemo;
Memo3: TMemo;
Memo4: TMemo;
Memo5: TMemo;
Memo6: TMemo;
ScrollBar1: TScrollBar;
ScrollBar2: TScrollBar;
ScrollBar3: TScrollBar;
ScrollBar4: TScrollBar;
ScrollBar5: TScrollBar;
ScrollBar6: TScrollBar;
procedure FormCreate(Sender: TObject);
procedure ScrollBarHChange(Sender: TObject);
procedure ScrollBarVChange(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var cycle: Integer;
begin
//GENERATE YOUR COMPONENTS HERE
//sets every components tag to its default top position
//(you can do this in any other way for example using array)
for cycle:=0 to Form1.ComponentCount-1 do
begin
if(Form1.Components[cycle] is TControl)then
Form1.Components[cycle].Tag:=(Form1.Components[cycle] as TControl).Top
end;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
//changes the panels sizes and positions
Panel1.Width:=Form1.ClientWidth div 3;
Panel2.Width:=Form1.ClientWidth div 3;
Panel3.Width:=Form1.ClientWidth div 3;
Panel2.Left:=Panel1.Width+1;
Panel3.Left:=Panel1.Width+Panel2.Width+1;
//if you dont want all scrollbars to reset on window resize, you need to handle the positioning of elements when window (and panels) size is changing
ScrollBar1.Position:=ScrollBar1.Min;
ScrollBar2.Position:=ScrollBar2.Min;
ScrollBar3.Position:=ScrollBar3.Min;
ScrollBar4.Position:=ScrollBar4.Min;
ScrollBar5.Position:=ScrollBar5.Min;
ScrollBar6.Position:=ScrollBar6.Min;
//make these tests on the widest element of each panel (8 is just a margin so the memo has some space on the right)
if((Memo1.Left+Memo1.Width)>(Panel1.ClientWidth-ScrollBar4.Width-8))then
begin
ScrollBar1.Enabled:=true;
ScrollBar1.Max:=Memo1.Width-Panel1.ClientWidth+Memo1.Left+ScrollBar4.Width+8;
end
else
ScrollBar1.Enabled:=false;
if((Memo3.Left+Memo3.Width)>(Panel2.ClientWidth-ScrollBar5.Width-8))then
begin
ScrollBar2.Enabled:=true;
ScrollBar2.Max:=Memo3.Width-Panel1.ClientWidth+Memo3.Left+ScrollBar5.Width+8;
end
else
begin
ScrollBar2.Position:=ScrollBar2.Min;
ScrollBar2.Enabled:=false;
end;
if((Memo5.Left+Memo5.Width)>(Panel3.ClientWidth-ScrollBar6.Width-8))then
begin
ScrollBar3.Enabled:=true;
ScrollBar3.Max:=Memo5.Width-Panel1.ClientWidth+Memo5.Left+ScrollBar6.Width+8;
end
else
ScrollBar3.Enabled:=false;
//make these tests on the bottom element of each panel (16 is just a margin so the memo has some space on the bottom)
if((Memo2.Top+Memo2.Height)>(Panel1.ClientHeight-ScrollBar1.Height-16))then
begin
ScrollBar4.Enabled:=true;
ScrollBar4.Max:=Memo2.Top+Memo2.Height-Panel1.ClientHeight+ScrollBar1.Height+16;
end
else
ScrollBar4.Enabled:=false;
if((Memo4.Top+Memo4.Height)>(Panel2.ClientHeight-ScrollBar2.Height-16))then
begin
ScrollBar5.Enabled:=true;
ScrollBar5.Max:=Memo4.Top+Memo4.Height-Panel2.ClientHeight+ScrollBar2.Height+16;
end
else
ScrollBar5.Enabled:=false;
if((Memo6.Top+Memo6.Height)>(Panel3.ClientHeight-ScrollBar3.Height-16))then
begin
ScrollBar6.Enabled:=true;
ScrollBar6.Max:=Memo6.Top+Memo6.Height-Panel3.ClientHeight+ScrollBar3.Height+16;
end
else
ScrollBar6.Enabled:=false;
end;
procedure TForm1.ScrollBarHChange(Sender: TObject);
var cycle: Integer;
begin
for cycle:=0 to ((Sender as TScrollBar).Parent as TPanel).ControlCount-1 do
begin
if(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] is TMemo)then
(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TMemo).Left:=0-(Sender as TScrollBar).Position+8;
end;
end;
procedure TForm1.ScrollBarVChange(Sender: TObject);
var cycle: Integer;
begin
for cycle:=0 to ((Sender as TScrollBar).Parent as TPanel).ControlCount-1 do
begin
if(not (((Sender as TScrollBar).Parent as TPanel).Controls[cycle] is TScrollBar))then
(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TControl).Top:=(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TControl).Tag-(Sender as TScrollBar).Position;
end;
end;
end.
和 .dfm:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 473
ClientWidth = 769
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnResize = FormResize
DesignSize = (
769
473)
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 257
Height = 473
Anchors = [akLeft, akTop, akBottom]
BevelOuter = bvNone
BorderStyle = bsSingle
TabOrder = 0
object Label1: TLabel
Left = 104
Top = 16
Width = 31
Height = 13
Caption = 'Label1'
end
object Label2: TLabel
Left = 104
Top = 152
Width = 31
Height = 13
Caption = 'Label2'
end
object Memo1: TMemo
Left = 8
Top = 32
Width = 497
Height = 89
Lines.Strings = (
'Memo1')
TabOrder = 0
end
object Memo2: TMemo
Left = 8
Top = 168
Width = 497
Height = 89
Lines.Strings = (
'Memo2')
TabOrder = 1
end
object ScrollBar1: TScrollBar
AlignWithMargins = True
Left = 0
Top = 452
Width = 236
Height = 17
Margins.Left = 0
Margins.Top = 0
Margins.Right = 17
Margins.Bottom = 0
Align = alBottom
PageSize = 0
TabOrder = 2
OnChange = ScrollBarHChange
ExplicitWidth = 253
end
object ScrollBar4: TScrollBar
Left = 236
Top = 0
Width = 17
Height = 452
Align = alRight
Enabled = False
Kind = sbVertical
PageSize = 0
TabOrder = 3
OnChange = ScrollBarVChange
ExplicitTop = 248
ExplicitHeight = 121
end
end
object Panel2: TPanel
Left = 256
Top = 0
Width = 257
Height = 473
Anchors = [akLeft, akTop, akBottom]
BevelOuter = bvNone
BorderStyle = bsSingle
TabOrder = 1
object Label3: TLabel
Left = 104
Top = 16
Width = 31
Height = 13
Caption = 'Label3'
end
object Label4: TLabel
Left = 104
Top = 152
Width = 31
Height = 13
Caption = 'Label4'
end
object Memo3: TMemo
Left = 8
Top = 32
Width = 497
Height = 89
Lines.Strings = (
'Memo3')
TabOrder = 0
end
object Memo4: TMemo
Left = 8
Top = 168
Width = 497
Height = 89
Lines.Strings = (
'Memo4')
TabOrder = 1
end
object ScrollBar2: TScrollBar
AlignWithMargins = True
Left = 0
Top = 452
Width = 236
Height = 17
Margins.Left = 0
Margins.Top = 0
Margins.Right = 17
Margins.Bottom = 0
Align = alBottom
PageSize = 0
TabOrder = 2
OnChange = ScrollBarHChange
ExplicitWidth = 253
end
object ScrollBar5: TScrollBar
Left = 236
Top = 0
Width = 17
Height = 452
Align = alRight
Enabled = False
Kind = sbVertical
PageSize = 0
TabOrder = 3
OnChange = ScrollBarVChange
ExplicitTop = 248
ExplicitHeight = 121
end
end
object Panel3: TPanel
Left = 512
Top = 0
Width = 257
Height = 473
Anchors = [akLeft, akTop, akBottom]
BevelOuter = bvNone
BorderStyle = bsSingle
TabOrder = 2
object Label5: TLabel
Left = 104
Top = 16
Width = 31
Height = 13
Caption = 'Label5'
end
object Label6: TLabel
Left = 104
Top = 152
Width = 31
Height = 13
Caption = 'Label6'
end
object Memo5: TMemo
Left = 8
Top = 32
Width = 497
Height = 89
Lines.Strings = (
'Memo5')
TabOrder = 0
end
object Memo6: TMemo
Left = 8
Top = 168
Width = 497
Height = 89
Lines.Strings = (
'Memo6')
TabOrder = 1
end
object ScrollBar3: TScrollBar
AlignWithMargins = True
Left = 0
Top = 452
Width = 236
Height = 17
Margins.Left = 0
Margins.Top = 0
Margins.Right = 17
Margins.Bottom = 0
Align = alBottom
PageSize = 0
TabOrder = 2
OnChange = ScrollBarHChange
ExplicitWidth = 253
end
object ScrollBar6: TScrollBar
Left = 236
Top = 0
Width = 17
Height = 452
Align = alRight
Enabled = False
Kind = sbVertical
PageSize = 0
TabOrder = 3
OnChange = ScrollBarVChange
ExplicitTop = 248
ExplicitHeight = 121
end
end
end
为什么不用两个滚动框。
您使用一个进行垂直滚动。在上面放置标签和带有备忘录的第二个滚动框。
第二个滚动框将在需要时用于水平滚动。
或者甚至更好的解决方案是将 TMemo 替换为其他一些控件,如 TRichEdit,它有自己的滚动条。所以你只有一个滚动框,就像现在一样,当文本太宽时,TRichEdit 会自行滚动。
我在 TScrollBox
中使用 TMemo
来显示一些文本,并在顶部使用 TLabel
作为 header 信息。有时备忘录比滚动框宽,当然 Horizontal scroll bar
可以用来左右滚动以查看备忘录中的文本。
我想要一个标签作为 header 始终以滚动框可见区域为中心。我可以通过设置 Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2);
来做到这一点并且它可以工作但是它有点闪烁,来回滚动时摇晃。备忘录移动流畅,标签移动不流畅。
这里是单位:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TScrollBox=Class(VCL.Forms.TScrollBox)
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
private
FOnScrollHorz: TNotifyEvent;
public
Property OnScrollHorz:TNotifyEvent read FOnScrollHorz Write FonScrollHorz;
End;
TForm1 = class(TForm)
ScrollBox1: TScrollBox;
Label1: TLabel;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure ScrollBox1Resize(Sender: TObject);
private
procedure MyScrollHorz(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TScrollBox.WMHScroll(var Message: TWMHScroll);
begin
inherited;
if Assigned(FOnScrollHorz) then FOnScrollHorz(Self);
end;
procedure TForm1.MyScrollHorz(Sender: TObject);
begin
Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2);
end;
procedure TForm1.ScrollBox1Resize(Sender: TObject);
begin
Label1.Left:= (Scrollbox1.Width div 2) - (Label1.Width div 2);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ScrollBox1.OnScrollHorz := MyScrollHorz;
end;
end.
和 dfm:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 212
ClientWidth = 458
Color = clBtnFace
DoubleBuffered = True
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object ScrollBox1: TScrollBox
Left = 0
Top = 0
Width = 458
Height = 212
HorzScrollBar.Smooth = True
HorzScrollBar.Tracking = True
Align = alClient
BiDiMode = bdLeftToRight
DoubleBuffered = True
ParentBiDiMode = False
ParentDoubleBuffered = False
TabOrder = 0
OnResize = ScrollBox1Resize
ExplicitHeight = 337
object Label1: TLabel
Left = 192
Top = 30
Width = 69
Height = 13
BiDiMode = bdLeftToRight
Caption = 'Details header'
ParentBiDiMode = False
end
object Memo1: TMemo
Left = 24
Top = 70
Width = 700
Height = 89
Lines.Strings = (
'Details...')
TabOrder = 0
end
end
end
我尝试使用 DoubleBuffered
但没有帮助。
有什么建议可以让 Label1 在没有 flickering/shaking 的情况下像滚动时的 Memo1 一样流畅吗?
编辑:
设计最终将是我在表单上有 3 个或滚动框,每个滚动框最多包含 3 个 header 的备忘录。滚动需要通过滚动框进行,因为同一滚动框中的所有备忘录都需要同时滚动。这意味着我看不出将标签放在表单或面板上然后放在滚动框外部的表单上如何工作:
编辑 2:
下面的答案确实提供了很好的解决方案,但它们确实需要将居中的 Labels
放置在 Scrollbox
之外并放在 Form
本身上。然后直接在 Form
上移动 Scrollbox's
scroll bars
或 scroll bars
。这确实得到了预期的效果,但它增加了一些不便,因为 Labels
不再是 Scrollbox
的一部分。
-" 备忘录移动流畅,标签不流畅。"
那是因为你试图阻止它移动。分离您的 OnScrollHorz
处理程序,标签将顺利移动。但这不是你想要的,它将不再以表格为中心。
问题是,在 inherited
调用 (WM_HSCROLL
) 期间,标签会随着备忘录一起移动。默认处理后,您重新定位标签,因此出现闪烁。
您可以公开一个额外的事件处理程序,它将在默认滚动 (OnBeforeHorzScroll) 之前触发,并在触发时隐藏标签。虽然 平滑地居中 ,但它会导致标签暂时消失的另一种闪烁。可能还是不尽如人意。
解决方案是使用窗体的父级控件,即滚动框的同级控件。你不能用 TLabel
来做,因为它是一个图形控件,但你可以使用 TStaticText
。如果 static 在设计时不小心放在滚动框后面,IDE 的 "structure pane" 可能会派上用场。
你可以这样做:
在您的表单上放置一个 ScrollBar,而不是 ScrollBox。将其对齐方式设置为底部(如果您希望有更多列或者您可以将每一列放在自己的面板中,则手动设置其大小和位置)。然后设置备忘录的大小并将标签放在表格的中心。设置备忘录的大小后(可能通过代码动态设置)放置此代码:
ScrollBar1.Min:=0-Memo1.Left;
ScrollBar1.Max:=Memo1.Width-Form1.ClientWidth+Memo1.Left;
最后就是设置ScrollBar的OnChange事件:
procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
Memo1.Left:=0-ScrollBar1.Position;
Memo2.Left:=0-ScrollBar1.Position;
...
MemoXY.Left:=0-ScrollBar1.Position;
end;
您的表单应如下所示:
完成!你有一个稳定的居中标签和平滑滚动的备忘录。
编辑:
这是一个版本,每个面板有 3 列,还带有垂直滚动条:
以及整个源代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Memo1: TMemo;
Memo2: TMemo;
Memo3: TMemo;
Memo4: TMemo;
Memo5: TMemo;
Memo6: TMemo;
ScrollBar1: TScrollBar;
ScrollBar2: TScrollBar;
ScrollBar3: TScrollBar;
ScrollBar4: TScrollBar;
ScrollBar5: TScrollBar;
ScrollBar6: TScrollBar;
procedure FormCreate(Sender: TObject);
procedure ScrollBarHChange(Sender: TObject);
procedure ScrollBarVChange(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var cycle: Integer;
begin
//GENERATE YOUR COMPONENTS HERE
//sets every components tag to its default top position
//(you can do this in any other way for example using array)
for cycle:=0 to Form1.ComponentCount-1 do
begin
if(Form1.Components[cycle] is TControl)then
Form1.Components[cycle].Tag:=(Form1.Components[cycle] as TControl).Top
end;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
//changes the panels sizes and positions
Panel1.Width:=Form1.ClientWidth div 3;
Panel2.Width:=Form1.ClientWidth div 3;
Panel3.Width:=Form1.ClientWidth div 3;
Panel2.Left:=Panel1.Width+1;
Panel3.Left:=Panel1.Width+Panel2.Width+1;
//if you dont want all scrollbars to reset on window resize, you need to handle the positioning of elements when window (and panels) size is changing
ScrollBar1.Position:=ScrollBar1.Min;
ScrollBar2.Position:=ScrollBar2.Min;
ScrollBar3.Position:=ScrollBar3.Min;
ScrollBar4.Position:=ScrollBar4.Min;
ScrollBar5.Position:=ScrollBar5.Min;
ScrollBar6.Position:=ScrollBar6.Min;
//make these tests on the widest element of each panel (8 is just a margin so the memo has some space on the right)
if((Memo1.Left+Memo1.Width)>(Panel1.ClientWidth-ScrollBar4.Width-8))then
begin
ScrollBar1.Enabled:=true;
ScrollBar1.Max:=Memo1.Width-Panel1.ClientWidth+Memo1.Left+ScrollBar4.Width+8;
end
else
ScrollBar1.Enabled:=false;
if((Memo3.Left+Memo3.Width)>(Panel2.ClientWidth-ScrollBar5.Width-8))then
begin
ScrollBar2.Enabled:=true;
ScrollBar2.Max:=Memo3.Width-Panel1.ClientWidth+Memo3.Left+ScrollBar5.Width+8;
end
else
begin
ScrollBar2.Position:=ScrollBar2.Min;
ScrollBar2.Enabled:=false;
end;
if((Memo5.Left+Memo5.Width)>(Panel3.ClientWidth-ScrollBar6.Width-8))then
begin
ScrollBar3.Enabled:=true;
ScrollBar3.Max:=Memo5.Width-Panel1.ClientWidth+Memo5.Left+ScrollBar6.Width+8;
end
else
ScrollBar3.Enabled:=false;
//make these tests on the bottom element of each panel (16 is just a margin so the memo has some space on the bottom)
if((Memo2.Top+Memo2.Height)>(Panel1.ClientHeight-ScrollBar1.Height-16))then
begin
ScrollBar4.Enabled:=true;
ScrollBar4.Max:=Memo2.Top+Memo2.Height-Panel1.ClientHeight+ScrollBar1.Height+16;
end
else
ScrollBar4.Enabled:=false;
if((Memo4.Top+Memo4.Height)>(Panel2.ClientHeight-ScrollBar2.Height-16))then
begin
ScrollBar5.Enabled:=true;
ScrollBar5.Max:=Memo4.Top+Memo4.Height-Panel2.ClientHeight+ScrollBar2.Height+16;
end
else
ScrollBar5.Enabled:=false;
if((Memo6.Top+Memo6.Height)>(Panel3.ClientHeight-ScrollBar3.Height-16))then
begin
ScrollBar6.Enabled:=true;
ScrollBar6.Max:=Memo6.Top+Memo6.Height-Panel3.ClientHeight+ScrollBar3.Height+16;
end
else
ScrollBar6.Enabled:=false;
end;
procedure TForm1.ScrollBarHChange(Sender: TObject);
var cycle: Integer;
begin
for cycle:=0 to ((Sender as TScrollBar).Parent as TPanel).ControlCount-1 do
begin
if(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] is TMemo)then
(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TMemo).Left:=0-(Sender as TScrollBar).Position+8;
end;
end;
procedure TForm1.ScrollBarVChange(Sender: TObject);
var cycle: Integer;
begin
for cycle:=0 to ((Sender as TScrollBar).Parent as TPanel).ControlCount-1 do
begin
if(not (((Sender as TScrollBar).Parent as TPanel).Controls[cycle] is TScrollBar))then
(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TControl).Top:=(((Sender as TScrollBar).Parent as TPanel).Controls[cycle] as TControl).Tag-(Sender as TScrollBar).Position;
end;
end;
end.
和 .dfm:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 473
ClientWidth = 769
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnResize = FormResize
DesignSize = (
769
473)
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 257
Height = 473
Anchors = [akLeft, akTop, akBottom]
BevelOuter = bvNone
BorderStyle = bsSingle
TabOrder = 0
object Label1: TLabel
Left = 104
Top = 16
Width = 31
Height = 13
Caption = 'Label1'
end
object Label2: TLabel
Left = 104
Top = 152
Width = 31
Height = 13
Caption = 'Label2'
end
object Memo1: TMemo
Left = 8
Top = 32
Width = 497
Height = 89
Lines.Strings = (
'Memo1')
TabOrder = 0
end
object Memo2: TMemo
Left = 8
Top = 168
Width = 497
Height = 89
Lines.Strings = (
'Memo2')
TabOrder = 1
end
object ScrollBar1: TScrollBar
AlignWithMargins = True
Left = 0
Top = 452
Width = 236
Height = 17
Margins.Left = 0
Margins.Top = 0
Margins.Right = 17
Margins.Bottom = 0
Align = alBottom
PageSize = 0
TabOrder = 2
OnChange = ScrollBarHChange
ExplicitWidth = 253
end
object ScrollBar4: TScrollBar
Left = 236
Top = 0
Width = 17
Height = 452
Align = alRight
Enabled = False
Kind = sbVertical
PageSize = 0
TabOrder = 3
OnChange = ScrollBarVChange
ExplicitTop = 248
ExplicitHeight = 121
end
end
object Panel2: TPanel
Left = 256
Top = 0
Width = 257
Height = 473
Anchors = [akLeft, akTop, akBottom]
BevelOuter = bvNone
BorderStyle = bsSingle
TabOrder = 1
object Label3: TLabel
Left = 104
Top = 16
Width = 31
Height = 13
Caption = 'Label3'
end
object Label4: TLabel
Left = 104
Top = 152
Width = 31
Height = 13
Caption = 'Label4'
end
object Memo3: TMemo
Left = 8
Top = 32
Width = 497
Height = 89
Lines.Strings = (
'Memo3')
TabOrder = 0
end
object Memo4: TMemo
Left = 8
Top = 168
Width = 497
Height = 89
Lines.Strings = (
'Memo4')
TabOrder = 1
end
object ScrollBar2: TScrollBar
AlignWithMargins = True
Left = 0
Top = 452
Width = 236
Height = 17
Margins.Left = 0
Margins.Top = 0
Margins.Right = 17
Margins.Bottom = 0
Align = alBottom
PageSize = 0
TabOrder = 2
OnChange = ScrollBarHChange
ExplicitWidth = 253
end
object ScrollBar5: TScrollBar
Left = 236
Top = 0
Width = 17
Height = 452
Align = alRight
Enabled = False
Kind = sbVertical
PageSize = 0
TabOrder = 3
OnChange = ScrollBarVChange
ExplicitTop = 248
ExplicitHeight = 121
end
end
object Panel3: TPanel
Left = 512
Top = 0
Width = 257
Height = 473
Anchors = [akLeft, akTop, akBottom]
BevelOuter = bvNone
BorderStyle = bsSingle
TabOrder = 2
object Label5: TLabel
Left = 104
Top = 16
Width = 31
Height = 13
Caption = 'Label5'
end
object Label6: TLabel
Left = 104
Top = 152
Width = 31
Height = 13
Caption = 'Label6'
end
object Memo5: TMemo
Left = 8
Top = 32
Width = 497
Height = 89
Lines.Strings = (
'Memo5')
TabOrder = 0
end
object Memo6: TMemo
Left = 8
Top = 168
Width = 497
Height = 89
Lines.Strings = (
'Memo6')
TabOrder = 1
end
object ScrollBar3: TScrollBar
AlignWithMargins = True
Left = 0
Top = 452
Width = 236
Height = 17
Margins.Left = 0
Margins.Top = 0
Margins.Right = 17
Margins.Bottom = 0
Align = alBottom
PageSize = 0
TabOrder = 2
OnChange = ScrollBarHChange
ExplicitWidth = 253
end
object ScrollBar6: TScrollBar
Left = 236
Top = 0
Width = 17
Height = 452
Align = alRight
Enabled = False
Kind = sbVertical
PageSize = 0
TabOrder = 3
OnChange = ScrollBarVChange
ExplicitTop = 248
ExplicitHeight = 121
end
end
end
为什么不用两个滚动框。
您使用一个进行垂直滚动。在上面放置标签和带有备忘录的第二个滚动框。
第二个滚动框将在需要时用于水平滚动。
或者甚至更好的解决方案是将 TMemo 替换为其他一些控件,如 TRichEdit,它有自己的滚动条。所以你只有一个滚动框,就像现在一样,当文本太宽时,TRichEdit 会自行滚动。