如何防止滚动框中的控件跳入焦点?

How to keep controls in a Scroll Box from jumping into focus?

我有一个垂直滚动的TScrollBox,水平滚动条被禁用。在这个滚动框内,有一个与顶部对齐的大面板,它的高度是根据其内容动态计算的。它的内容是两个面板,一个向左对齐,另一个向客户端对齐,中间有一个分隔线。在这两个面板中的每一个内部,都有一系列用户选择的面板与顶部对齐,它们也可以动态调整大小,因此 extending/reducing 滚动框内主面板的高度。

procedure TfrmDashboard.ResizePanels;
var
  X: Integer;
  H1, H2: Integer;
  H: Integer;
begin
  H1:= 0;
  H2:= 0;
  //Calculate height of left panel
  for X := 0 to p1.ControlCount-1 do
    H1:= H1 + p1.Controls[X].Height;
  //Calculate height of right panel
  for X := 0 to p2.ControlCount-1 do
    H2:= H2 + p2.Controls[X].Height;
  //Check which panel is larger
  H:= H1;
  if H2 > H then
    H:= H2;
  //Adjust scrolling height
  pMain.Height:= H + 10;
  SB.VertScrollBar.Range:= pMain.Height;
  SB.VertScrollBar.Size:= pMain.Height;
end;

同时,这些较小的内容子面板中的大多数也有接收焦点的控件,并且需要允许获得焦点。目前滚动一切正常。

当用户单击滚动框中的此类可聚焦控件之一时,就会出现问题。如果该控件碰巧被部分隐藏(控件的顶部超出滚动位置的顶部),则整个滚动框会跳起来将此控件定位在滚动框的顶部。

这似乎是 "feature",但我想禁用该功能。在这种情况下,这很烦人。如何防止滚动框在其中一个子项获得焦点时跳转位置?

这是TScrollBox的默认行为,没有属性关闭这个功能。
但是您可以通过 subclassing TScrollBox 更改此行为并改用这个新的 class:

TModifiedScrollBox=class(TScrollBox)
protected
    procedure AutoScrollInView(AControl:TControl); override;
end;

procedure TModifiedScrollBox.AutoScrollInView(AControl:TControl);
begin
  // empty body
end;

此代码的缺点是,如果您在自己的代码中使用它,AutoScrollInView 将停止工作。但是如果你仍然需要这个方法,你可以创建额外的方法来 "save" 它:

TModifiedScrollBox=class(TScrollBox)
protected
    procedure AutoScrollInView(AControl:TControl); override;
    procedure AutoScrollInViewSave(AControl:TControl);
end;

procedure TModifiedScrollBox.AutoScrollInViewSave(AControl:TControl);
begin
  // forward to base implementation
  inherited AutoScrollInView(AControl);
end;

然后在您的代码中,您应该为这个新 class 替换对 AutoScrollInViewSave 的所有 AutoScrollInView 调用。

PS 在后台,每次新控件获得焦点时,TCustomForm 都会为所有获得焦点的控件的父级调用 AutoScrollInView。