【问题标题】:No scrollbars appear in an autoscrollable form没有滚动条出现在可自动滚动的表单中
【发布时间】:2017-08-23 21:06:21
【问题描述】:

-------------- 原始问题-------------------- -----

向所有 Delphi 开发人员致敬!在 Delphi 2006 非 MDI 应用程序中,我创建了一个不可调整大小、可自动滚动、可自动调整大小的表单。这是表单单元的摘录:

uses Grid;

TGridFrm = class(TForm)
    public
        Grid : TGrid;
        constructor Create(AOwner : TComponent; Asize : TPoint);
end;

implementation

constructor TGridFrm.Create(AOwner: TComponent; Asize : TPoint);
begin
    inherited Create(aowner);
    borderstyle := bsSingle; // users are not allowed to resize the form
    windowstate := wsNormal;
    borderwidth := 0;
    autosize := True;
    autoscroll := True;
    constraints.maxwidth := screen.width - 1;
    constraints.maxheight := screen.height - 1;
    grid := TGrid.Create(asize.x, asize.y, self);
end;

现在,TGrid 当然是一个带有自己画布的自定义控件。这是其单元的摘录:

TGrid = class (TCustomControl)
    public
        NoOfCellsX,
        NoOfCellsY,
        CellSize : integer;
        procedure SetZoom(z : integer);
        constructor Create(AWidth, AHeight : Integer; AParent : TForm = nil);
end;

implementation

constructor TGrid.Create(AWidth, AHeight : Integer; AParent : TForm = nil);
begin
    inherited Create(AParent);
    Parent := AParent;
    align := alCustom;
    left := 0;
    top := 0;
end;

procedure TGrid.SetZoom(zoom : integer);
begin
    cellsize := zoom * 10 div 100;
    width := noofcellsx * cellsize;
    height := noofcellsy * cellsize;
end;

在表单的单元中,我已经安排了一些事情(通过 ApplicationEvents 对象),以便在按下数字 +/- 键时使用一些缩放值调用 SetZoom。这一切背后的想法是让我的自定义控件捕捉到表单的左上角(具有一些预定义的边距/边框宽度),并让整个表单在我放大或缩小自定义控件时自动调整其大小,但是永远不会超出屏幕限制。它可以工作,但只能达到滚动条必须可见的程度:它们从不出现。由于这是一个可自动滚动的表单,当表单内的控件(在这种情况下为网格)变得大于受约束的表单并在它变小时时让开时,它们是否应该出现?我什至尝试通过将 SetZoom 移动到表单的类进行一些重构,但无济于事。我在这里错过了什么?

----------------- 之后添加的可编译代码------------------

项目文件:

program MyApp;

uses
    Forms,
    Grid in 'Source\Grid.pas',
    GridForm in 'Source\GridForm.pas' {GridFrm},
    Main in 'Source\Main.pas' {MainFrm};

{$R *.res}

begin
    Application.Initialize;
    Application.CreateForm(TMainFrm, MainFrm);
    Application.Run;
end.

Main.pas:

unit Main;

interface

uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
    Forms, Dialogs, StdCtrls;

type
    TMainFrm = class(TForm)
        CreateNewFormButton: TButton;
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure CreateNewFormButtonClick(Sender: TObject);
    end;

var
    MainFrm: TMainFrm;

implementation

{$R *.dfm}

uses
    GridForm;

procedure TMainFrm.CreateNewFormButtonClick(Sender: TObject);
var aform : TForm;
begin
    aform := TGridFrm.Create(self, point(15, 15));
    aform.show;
    tgridfrm(aform).grid.SetZoom(100);
end;

procedure TMainFrm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    Action := caFree;
end;

end.

GridForm.pas:

unit GridForm;

interface

uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, Grid, AppEvnts;

type
    TGridFrm = class(TForm)
        ApplicationEvents1: TApplicationEvents;
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
    private
        TheGrid : TGrid;
    public
        property Grid : TGrid READ TheGrid WRITE TheGrid;
        constructor Create(AOwner : TComponent; ASize : TPoint);
    end;

var
    GridFrm: TGridFrm;

implementation

{$R *.dfm}

procedure TGridFrm.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
var keystate : TKeyboardState;
begin
    if not Active then begin exit; end;
    if msg.message = WM_KEYDOWN then
    begin
        getkeyboardstate(keystate);
        case msg.wparam of

            vk_Add : begin // zoom in
                grid.setzoom(grid.zoom + 10);
                handled := True;
            end;

            vk_Subtract : begin // zoom out
                grid.setzoom(grid.zoom - 10);
                handled := True;
            end;

            // other keys down here...

        end;
    end;
end;

constructor TGridFrm.Create(AOwner : TComponent; ASize : TPoint);
begin
    inherited Create(AOwner);
    borderstyle := bsSingle;
    borderwidth := 2;
    autosize := True;
    autoscroll := True;
    constraints.maxwidth := screen.width - 1;
    constraints.maxheight := screen.height - 1;
    visible := False;
    grid := TGrid.Create(asize.x, asize.y, random(800) + 500, self);
end;

procedure TGridFrm.FormClose(Sender: TObject; var Action: TCloseAction);
begin                
    Action := caFree;
end;

end.

还有 Grid.pas:

unit Grid;

interface

uses
    StdCtrls, SysUtils, Controls, Forms, Graphics, Dialogs;

type
    TGrid = class (TCustomControl)
        Lbl1, Lbl2,
        GridSizeInfoLbl,
        FormSizeInfoLbl,
        WarningLbl : TLabel;

        public
            NoOfCellsX,
            NoOfCellsY,
            SquareSize, // in 1/1000ths of centimeter
            CellSize, // in pixels
            Zoom : integer;
            procedure SetZoom(z : integer);
            constructor Create(x, y, asquaresize : integer; AParent : TForm = nil);
    end;

implementation

uses
    GridForm;

constructor TGrid.Create(x, y, asquaresize : integer; AParent : TForm = nil);
begin
    inherited Create(AParent);
    parent := AParent;
    color := clTeal;
    align := alCustom;
    left := 0;
    top := 0;
    noofcellsx := x;
    noofcellsy := y;
    squaresize := asquaresize;
    Lbl1 := TLabel.Create(self);
    Lbl2 := TLabel.Create(self);
    GridSizeInfoLbl := TLabel.Create(self);
    FormSizeInfoLbl := TLabel.Create(self);
    WarningLbl := TLabel.Create(self);
    with Lbl1 do
    begin
        parent := self;
        caption := 'Size of grid: ';
        width := 55;
        height := 18;
        left := 2;
        top := 1;
    end;
    with Lbl2 do
    begin
        parent := self;
        caption := 'Size of form: ';
        width := 75;
        height := 18;
        left := 2;
        top := 19;
    end;
    with GridSizeInfoLbl do
    begin
        parent := self;
        width := 100;
        height := 18;
        left := 65;
        top := 1;
    end;
    with FormSizeInfoLbl do
    begin
        parent := self;
        width := 100;
        height := 18;
        left := 65;
        top := 19;
    end;
    with WarningLbl do
    begin
        parent := self;
        width := 150;
        height := 18;
        left := 2;
        top := 39;
    end;
end;

procedure TGrid.SetZoom(z : integer);
begin
    zoom := z;
    cellsize := (screen.pixelsperinch * squaresize * zoom) div (1000 * 254);
    width := noofcellsx * cellsize;
    height := noofcellsy * cellsize;

    GridSizeInfoLbl.caption := inttostr(Width) +
        'x' + inttostr(Height) +
        ' (zoom: ' + inttostr(zoom) +
        ', cellsize zoomed: ' + inttostr(cellsize) +
        ', squaresize: ' + inttostr(squaresize) +
        'mm, squares: ' + inttostr(noofcellsx) + 'x' + inttostr(noofcellsy) + ')';
    with tgridfrm(parent) do
    begin
        left := (screen.Width - width) div 2;
        top := (screen.Height - height) div 2;
        FormSizeInfoLbl.caption := inttostr(Width) + 'x' + inttostr(Height) +
        ' (clientarea: ' + inttostr(clientwidth) + 'x' + inttostr(clientheight) + ')';
        if self.width > clientwidth then
            if self.Height > clientheight then
                warninglbl.caption := 'Both scrollbars should appear!'
            else
                warninglbl.caption := 'Horizontal scrollbar should appear!'
        else if self.Height > clientheight then
            warninglbl.caption := 'Vertical scrollbar should appear!'
        else
            warninglbl.caption := 'No scrollbars needed';
    end;
end;

end.

代码概要:单击主窗体的按钮会创建一个可自动调整大小的窗体,而该窗体又会创建一个具有随机初始大小的子网格。数字 +/- 键使网格变大或变小,并且表单会相应地自动调整大小,但永远不会显示滚动条,无论网格变得多大(我添加的标签提供视觉反馈)。

【问题讨论】:

  • 自动调整大小和自动滚动?我会想象他们会互相打架。它们都受到完全相同的影响:控件放置在其边界之外。
  • 发布可编译的代码,让我们看看是不是问题。
  • @SertacAkyuz 我编写了重现问题的代码。我应该怎么把它贴在这里?可以将其发布为我的问题的答案还是违反规则?抱歉,我是新来的。
  • 在问题本身中包含一个最小示例,请参阅minimal reproducible example
  • 感谢您更新代码,但它仍然不是 MCVE。据我所知,您的问题似乎与此自定义网格控件无关,而是与它的容器形式有关。这同样适用于 any 控件,对吧?这就是 MCVE 的目的,消除实际上与问题无关的所有内容,特别是“最小”部分。在创建 MCVE 的过程中,您很可能会自己发现问题,因为您无需阅读大量不相关的代码。

标签: delphi


【解决方案1】:

您的问题是双重的。

第一个是,正如 Jerry 对问题所评论的那样,AutoSize。 autosize 的目的是调整表单的大小以使内容可见。当所有内容都可见时,不可能没有滚动条,因此这两个属性显然是矛盾的。

因此,VCL 开发人员已经采取了预防措施。以下来自 D2007 来源:

function TScrollingWinControl.AutoScrollEnabled: Boolean;
begin
  Result := not AutoSize and not (DockSite and UseDockManager);
end;

如您所见,当设置AutoSize 时,设置AutoScroll 无效。

你可以覆盖这个行为,这是一个虚方法,如果它不会干扰第二次折叠的话。


既然您已经决定关闭自动调整大小并根据工作区大小自行计算和设置表单的所需大小,请解决您的第二个问题:网格控件的对齐方式。

下面是垂直滚动条要查看是否需要调整时的D2007代码:

  procedure ProcessVert(Control: TControl);
  begin
    if Control.Visible then
      case Control.Align of
        alTop, alNone:
          if (Control.Align = alTop) or (Control.Anchors * [akTop, akBottom] = [akTop]) then
            NewRange := Max(NewRange, Position + Control.Top + Control.Height);
        alBottom: Inc(AlignMargin, Control.Height);
      end;
  end;

如您所见,如果控件没有alTopalBottomalNone 对齐方式,则它不会对自动垂直滚动条产生影响。你有alCustom

这也是为什么覆盖自动调整大小行为无济于事,AutoSize 依赖于具有“左”、“右”、“上”、“下”或“无”对齐控件的控件。


您必须考虑 VCL 内部的工作方式来重新设计您的控件。并非所有内部依赖方面都可以记录,因此您必须使用 source 进行这种增强 开发。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    • 2020-02-02
    • 2011-10-02
    • 2012-07-09
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    相关资源
    最近更新 更多