使用拆分器在父级工作区之外调整控件大小



我正试图复制使用拆分器调整大小的操作,就像本问题中描述的那样,由MS SQL Managment Studio完成。

所以我有一个ScrollBox,有很多面板和拆分器对,垂直堆叠在一起。当我想用相应的拆分器放大面板时,它会将可能的增长限制在滚动框中的剩余大小。我无法将拆分器拖动到滚动框的客户端大小之外。

有人能帮我解决这个问题吗?

我尝试放大滚动框的VertScrollBar.Range,但没有成功:

unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
  tDataBlock = class(TComponent)
    fPanel: TPanel;
    fLabel: TLabel;
    fSplitter: TSplitter;
    fOwner: TWinControl;
  published
    property Panel: TPanel    read fPanel   write fPanel;
    property Text: TLabel   read fLabel   write fLabel;
    property Owner: TWinControl read fOwner write fOwner;
  public
    constructor Create(Owner: TWinControl; var t: integer);
  end;
  TForm1 = class(TForm)
    ScrollBox1: TScrollBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    BlockCount: integer;
    procedure ConfigureScreen;
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ConfigureScreen;
var i: integer;
    TotalHeight: integer;
begin
  TotalHeight := 0;
  for I := 0 to ScrollBox1.ComponentCount - 1 do begin
    if ScrollBox1.Components[i] is TPanel then
      TotalHeight := TotalHeight + TPanel(ScrollBox1.Components[i]).Height;
    if ScrollBox1.Components[i] is TSplitter then
      TotalHeight := TotalHeight + TSplitter(ScrollBox1.Components[i]).Height;
  end;
  ScrollBox1.VertScrollBar.Range := TotalHeight;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  db: tDataBlock;
  t: integer;
begin
  t := 0;
  BlockCount := 0;
  for I := 0 to 3 do begin
    db := tDataBlock.Create(ScrollBox1, t);
    Inc(BlockCount);
  end;
  ConfigureScreen;
end;
{ tDataBlock }
constructor tDataBlock.Create(Owner: TWinControl; var t: integer);
begin
  fOwner := Owner;
  fPanel := TPanel.Create(Owner);
  fPanel.Parent := Owner;
  fPanel.Height := 150;
  fPanel.Top := t;
  fPanel.Align := alTop;
  fPanel.AlignWithMargins := false;
  fPanel.Color := clRed;
  fPanel.ParentBackground := false;
  fPanel.BorderWidth := 0;
  fPanel.BorderStyle := bsNone;
  fPanel.Ctl3D := false;
  fPanel.AutoSize := false;
  fPanel.UseDockManager := false;
  t := fPanel.Top + Panel.Height + 1;
  fLabel := TLabel.Create(self);
  fLabel.Parent := fPanel;
  fLabel.Align := altop;
  fLabel.Caption := inttostr(fPanel.Height);
  fLabel.Font.Size := 10;
  fSplitter := TSplitter.Create(Owner);
  fSplitter.Parent:= Owner;
  fSplitter.Height := 3;
  fsplitter.Top := t;
  fSplitter.AutoSnap := false;
  fSplitter.AlignWithMargins := false;
  fSplitter.MinSize := 1;
  fSplitter.Align := alTop;
  t := fSplitter.Top + fSplitter.Height + 1;
end;
end.

正如SilverWarior所提到的,TSplitter的目的是划分客户端区域,而不是将相邻控件调整为您想要的任何大小。为了实现你的目标,你需要通过一些技巧来改变它的行为,就像NGLN建议的那样。但如果这样做,您可能会遇到一些副作用,因为代码的其他部分可能需要ScrollBox的ClientRect

另一种选择是简单地使用鼠标事件来模拟TSplitter的行为。我稍微更改了您的代码,改为使用TPanel。但这只是一个快速的开始,您可能需要更多的编码,例如消除闪烁;(。

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics,
  Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
type
  tDataBlock = class(TComponent)
  private
    fPanel: TPanel;
    fLabel: TLabel;
    fResizingPanel: TPanel;
    fOwner: TWinControl;
    IsResizing: Boolean;
    StartHeight, StartY: Integer;
    procedure ResizingPanelMouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    procedure ResizingPanelMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ResizingPanelMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  published
    property Panel: TPanel read fPanel write fPanel;
    property Text: TLabel read fLabel write fLabel;
    property Owner: TWinControl read fOwner write fOwner;
  public
    constructor Create(Owner: TWinControl; var t: Integer);
  end;
  TForm1 = class(TForm)
    ScrollBox1: TScrollBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    BlockCount: Integer;
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  db: tDataBlock;
  t: Integer;
begin
  t := 0;
  BlockCount := 0;
  for I := 0 to 3 do
  begin
    db := tDataBlock.Create(ScrollBox1, t);
    Inc(BlockCount);
  end;
end;
{ tDataBlock }
constructor tDataBlock.Create(Owner: TWinControl; var t: Integer);
begin
  fOwner := Owner;
  fPanel := TPanel.Create(Owner);
  fPanel.Parent := Owner;
  fPanel.Height := 150;
  fPanel.Top := t;
  fPanel.Align := alTop;
  fPanel.AlignWithMargins := False;
  fPanel.Color := clRed;
  fPanel.ParentBackground := False;
  fPanel.BorderWidth := 0;
  fPanel.BorderStyle := bsNone;
  fPanel.Ctl3D := False;
  fPanel.AutoSize := False;
  fPanel.UseDockManager := False;
  fPanel.Constraints.MinHeight := 50;
  // fPanel.DoubleBuffered := True;
  t := fPanel.Top + Panel.Height + 1;
  fLabel := TLabel.Create(self);
  fLabel.Parent := fPanel;
  fLabel.Align := alTop;
  fLabel.Caption := inttostr(fPanel.Height);
  fLabel.Font.Size := 10;
  fResizingPanel := TPanel.Create(Owner);
  fResizingPanel.Parent := Panel;
  fResizingPanel.Height := 10;
  fResizingPanel.Align := alBottom;
  fResizingPanel.AlignWithMargins := False;
  fResizingPanel.ParentBackground := False;
  fResizingPanel.Cursor := crVSplit;
  fResizingPanel.OnMouseDown := ResizingPanelMouseDown;
  fResizingPanel.OnMouseMove := ResizingPanelMouseMove;
  fResizingPanel.OnMouseUp := ResizingPanelMouseUp;
  t := fResizingPanel.Top + fResizingPanel.Height + 1;
end;
procedure tDataBlock.ResizingPanelMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  IsResizing := True;
  StartHeight := Panel.Height;
  StartY := fResizingPanel.ClientOrigin.Y + Y;
end;
procedure tDataBlock.ResizingPanelMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
  if IsResizing then
    Panel.Height := StartHeight + fResizingPanel.ClientOrigin.Y + Y - StartY;
end;
procedure tDataBlock.ResizingPanelMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  IsResizing := False;
end;
end.

Delphi的默认VCL TSplitter不支持超出父客户端rect大小的大小调整。(如果父级有滚动条,我会选择这样的更改,但除此之外。(原因在于TSplitter.MouseDown,其中根据父级的ClientHeight设置了专用字段FMaxSize

一个可能只适用于TScrollBox的解决方案是通过返回其父级的不同ClientHeight来欺骗TSplitter,如下所示:

type
  TScrollBox = class(Vcl.Forms.TScrollBox)
  protected
    function GetClientRect: TRect; override;
  end;
implementation
function TScrollBox.GetClientRect: TRect;
begin
  Result := inherited GetClientRect;
  if GetCaptureControl is TSplitter then
    Result.Bottom := Screen.DesktopHeight;
end;

最新更新