Delphi 带按钮的Stringgrid

Delphi 带按钮的Stringgrid,delphi,tstringgrid,Delphi,Tstringgrid,第一个问题: 如何调用stringgrid中不可见的零件?您需要滚动查看它。 例如: stringgrid中有20行,但一次只能看到10行。您需要滚动查看其他10个。“隐藏”的是什么 第二个问题: 我知道这可能不是正确的方法,因此请提供一些建议。 我有一个固定行的字符串网格。我在运行时添加颜色按钮。所以我用按钮填充了1列。 我使用此按钮“插入/删除”行。只要所有的网格都在“可见”部分,它就可以正常工作。 当我“插入”新行并将按钮移动到“隐藏”部分时,会出现问题。最后一个按钮被绘制到单元格[0,0

第一个问题:

如何调用stringgrid中不可见的零件?您需要滚动查看它。
例如:
stringgrid中有20行,但一次只能看到10行。您需要滚动查看其他10个。“隐藏”的是什么

第二个问题:

我知道这可能不是正确的方法,因此请提供一些建议。
我有一个固定行的字符串网格。我在运行时添加颜色按钮。所以我用按钮填充了1列。 我使用此按钮“插入/删除”行。只要所有的网格都在“可见”部分,它就可以正常工作。 当我“插入”新行并将按钮移动到“隐藏”部分时,会出现问题。最后一个按钮被绘制到单元格[0,0]。“隐藏”部分中的其他按钮绘制正确。知道为什么会这样吗?我应该用OnDraw方法解决这个问题,还是有更好(正确)的方法

代码:

  • 对于可见部分,存在
    VisibleRowCount
    VisibleColCount
    属性。
    TGridAxisDrawInfo
    记录类型将可见零件边界和所有零件一起命名为区段(反之亦然,我不记得了)。因此,对于字符串网格的不可见部分,VCL声明的名称没有特定的限制。这只是看不见的部分

  • 我认为您犯了一个逻辑错误:滚动网格时按钮未移动。尽管看起来它们可能会移动,但这只是由于内部调用
    滚动窗口
    而移动设备上下文内容的结果。字符串网格组件中的滚动条是自定义添加的,其工作方式与例如
    TScrollBox
    的滚动条不同

    要始终显示实际位置上的所有按钮,请在
    OnTopLeftChanged
    事件中重新绘制字符串网格:

    procedure TForm1.StringGrid1TopLeftChanged(Sender: TObject);
    begin
      StringGrid1.Repaint;
    end;
    
    当所有行的行高和字符串网格的高度从未更改时,仅创建一次所有按钮就足够了,并让它们保持不变。这意味着每个按钮不再“附加”到一行,并且将它们存储在
    对象
    属性中不再具有任何意义。按下按钮时,只需从按钮的位置结合字符串网格的
    TopRow
    属性计算所需的行索引,该属性指定网格中第一个可见可滚动行的索引

    如果网格可以调整大小(例如通过锚定),则更新父级的OnResize事件中的按钮计数。如果字符串网格的行数可能小于最大可见行数,则还应更新(可见)按钮数

    如果您想要更多的答案,请更新您的问题,并解释由于与网格和/或按钮的交互,如何调用
    MoveRowPlus
    MoveRowMinus
    例程,因为现在我不完全理解您想要的是什么

    关于
    CellRect
    给出了错误的坐标:这是因为
    CellRect
    仅适用于全部(或部分)可见单元格。引述:

    如果指示的单元格不可见,
    CellRect
    返回一个空矩形


  • 由于OP的评论而增加的内容

    我认为下面的代码符合您的要求。每个按钮的原始行索引存储在
    标记
    属性中

    unit Unit1;
    
    interface
    
    uses
      Windows, Classes, Controls, Forms, StdCtrls, Grids;
    
    type
      TForm1 = class(TForm)
        Grid: TStringGrid;
        procedure GridTopLeftChanged(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        FPrevTopRow: Integer;
        procedure CreateGridButtons(ACol: Integer);
        procedure GridButtonClick(Sender: TObject);
        procedure RearrangeGridButtons;
        function GetInsertRowCount(ARow: Integer): Integer;
        function GridButtonToRow(AButton: TButton): Integer;
        procedure MoveGridButtons(ButtonIndex, ARowCount: Integer);
      end;
    
    implementation
    
    {$R *.dfm}
    
    type
      TStringGridAccess = class(TStringGrid);
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FPrevTopRow := Grid.TopRow;
      CreateGridButtons(2);
    end;
    
    procedure TForm1.CreateGridButtons(ACol: Integer);
    var
      R: TRect;
      I: Integer;
      Button: TButton;
    begin
      R := Grid.CellRect(ACol, Grid.FixedRows);
      Inc(R.Right, Grid.GridLineWidth);
      Inc(R.Bottom, Grid.GridLineWidth);
      for I := Grid.FixedRows to Grid.RowCount - 1 do
      begin
        Button := TButton.Create(Grid);
        Button.BoundsRect := R;
        Button.Caption := '+';
        Button.Tag := I;
        Button.ControlStyle := [csClickEvents];
        Button.OnClick := GridButtonClick;
        Button.Parent := Grid;
        Grid.Objects[0, I] := Button;
        OffsetRect(R, 0, Grid.DefaultRowHeight + Grid.GridLineWidth);
      end;
    end;
    
    procedure TForm1.GridButtonClick(Sender: TObject);
    var
      Button: TButton absolute Sender;
      N: Integer;
      I: Integer;
    begin
      N := GetInsertRowCount(Button.Tag);
      if Button.Caption = '+' then
      begin
        Button.Caption := '-';
        Grid.RowCount := Grid.RowCount + N;
        for I := 1 to N do
          TStringGridAccess(Grid).MoveRow(Grid.RowCount - 1,
            GridButtonToRow(Button) + 1);
        MoveGridButtons(Button.Tag, N);
      end
      else
      begin
        Button.Caption := '+';
        for I := 1 to N do
          TStringGridAccess(Grid).MoveRow(GridButtonToRow(Button) + 1,
            Grid.RowCount - 1);
        Grid.RowCount := Grid.RowCount - N;
        MoveGridButtons(Button.Tag, -N);
      end;
    end;
    
    procedure TForm1.GridTopLeftChanged(Sender: TObject);
    begin
      RearrangeGridButtons;
      FPrevTopRow := Grid.TopRow;
    end;
    
    procedure TForm1.RearrangeGridButtons;
    var
      I: Integer;
      Shift: Integer;
    begin
      Shift := (Grid.TopRow - FPrevTopRow) *
        (Grid.DefaultRowHeight + Grid.GridLineWidth);
      for I := 0 to Grid.ControlCount - 1 do
      begin
        Grid.Controls[I].Top := Grid.Controls[I].Top - Shift;
        Grid.Controls[I].Visible := Grid.Controls[I].Top > 0;
      end;
    end;
    
    function TForm1.GetInsertRowCount(ARow: Integer): Integer;
    begin
      //This function should return the number of rows which is to be inserted
      //below ARow. Note that ARow refers to the original row index, that is:
      //without account for already inserted rows. For now, assume three rows:
      Result := 3;
    end;
    
    function TForm1.GridButtonToRow(AButton: TButton): Integer;
    begin
      for Result := 0 to Grid.RowCount - 1 do
        if Grid.Objects[0, Result] = AButton then
          Exit;
      Result := -1;
    end;
    
    procedure TForm1.MoveGridButtons(ButtonIndex, ARowCount: Integer);
    var
      I: Integer;
    begin
      for I := 0 to Grid.ControlCount - 1 do
        if Grid.Controls[I].Tag > ButtonIndex then
          Grid.Controls[I].Top := Grid.Controls[I].Top +
            ARowCount * (Grid.DefaultRowHeight + Grid.GridLineWidth);
    end;
    
    end.
    

    但是,我可以说,不使用按钮控件也可以做到这一点:我建议在字符串网格的OnDrawCell事件中绘制假按钮控件。

    好的,我尝试访问OnDrawCell中的按钮,但出现“访问被拒绝”错误。我尝试将按钮停靠到网格单元,但最后一个可见按钮的高度会降低到网格的可见部分。当然,最后一个按钮被画到单元格[0,0](注释1/2)谢谢你的回答。我今天要试试。我知道我有点不清楚我想要实现什么。我想用数据和按钮填充网格。当用户单击按钮(标题:='+')时,将使用以下参数调用MoveRowPlus:Grid、ARow(按钮所在的行)和stRow(需要在ARow下插入的行数)。我有20行20个按钮。我点击第3行中的按钮,移动第4到20行进行stRow。如果stRow为2,则第4行变为第6行,依此类推,直到网格结束。(按钮与其指定的行一起移动)然后填充2个空行(注释2/2)…然后用“子查询”填充2个空行。按钮的数量保持不变。当我再次单击按钮(现在标题为“=”-”)时,将调用MoveRowMinus,网格将返回初始状态。(子查询关闭)。我希望我对我想实现的目标已经足够清楚了。您认为不使用对象属性就可以实现这一点吗?(在NGLN-对不起,atName似乎不起作用)(atNGLN)谢谢。这正是我需要的。我曾考虑自己画假纽扣,但这个解决方案似乎好得多。我对使用这种方法的“利弊”很感兴趣。你为什么推荐使用onDraw?谢谢你的时间和情报。我Bagon@user805528自己绘制按钮的专业版:无需因滚动而重新排列控件,行可以具有不同的高度,所需资源更少,网格闪烁更少,更容易处理行数更改、行插入和行删除,而且更容易在自定义衍生工具中实现。这很好,但当StringGrid有10000行时,我的应用程序崩溃了!!
    unit Unit1;
    
    interface
    
    uses
      Windows, Classes, Controls, Forms, StdCtrls, Grids;
    
    type
      TForm1 = class(TForm)
        Grid: TStringGrid;
        procedure GridTopLeftChanged(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        FPrevTopRow: Integer;
        procedure CreateGridButtons(ACol: Integer);
        procedure GridButtonClick(Sender: TObject);
        procedure RearrangeGridButtons;
        function GetInsertRowCount(ARow: Integer): Integer;
        function GridButtonToRow(AButton: TButton): Integer;
        procedure MoveGridButtons(ButtonIndex, ARowCount: Integer);
      end;
    
    implementation
    
    {$R *.dfm}
    
    type
      TStringGridAccess = class(TStringGrid);
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FPrevTopRow := Grid.TopRow;
      CreateGridButtons(2);
    end;
    
    procedure TForm1.CreateGridButtons(ACol: Integer);
    var
      R: TRect;
      I: Integer;
      Button: TButton;
    begin
      R := Grid.CellRect(ACol, Grid.FixedRows);
      Inc(R.Right, Grid.GridLineWidth);
      Inc(R.Bottom, Grid.GridLineWidth);
      for I := Grid.FixedRows to Grid.RowCount - 1 do
      begin
        Button := TButton.Create(Grid);
        Button.BoundsRect := R;
        Button.Caption := '+';
        Button.Tag := I;
        Button.ControlStyle := [csClickEvents];
        Button.OnClick := GridButtonClick;
        Button.Parent := Grid;
        Grid.Objects[0, I] := Button;
        OffsetRect(R, 0, Grid.DefaultRowHeight + Grid.GridLineWidth);
      end;
    end;
    
    procedure TForm1.GridButtonClick(Sender: TObject);
    var
      Button: TButton absolute Sender;
      N: Integer;
      I: Integer;
    begin
      N := GetInsertRowCount(Button.Tag);
      if Button.Caption = '+' then
      begin
        Button.Caption := '-';
        Grid.RowCount := Grid.RowCount + N;
        for I := 1 to N do
          TStringGridAccess(Grid).MoveRow(Grid.RowCount - 1,
            GridButtonToRow(Button) + 1);
        MoveGridButtons(Button.Tag, N);
      end
      else
      begin
        Button.Caption := '+';
        for I := 1 to N do
          TStringGridAccess(Grid).MoveRow(GridButtonToRow(Button) + 1,
            Grid.RowCount - 1);
        Grid.RowCount := Grid.RowCount - N;
        MoveGridButtons(Button.Tag, -N);
      end;
    end;
    
    procedure TForm1.GridTopLeftChanged(Sender: TObject);
    begin
      RearrangeGridButtons;
      FPrevTopRow := Grid.TopRow;
    end;
    
    procedure TForm1.RearrangeGridButtons;
    var
      I: Integer;
      Shift: Integer;
    begin
      Shift := (Grid.TopRow - FPrevTopRow) *
        (Grid.DefaultRowHeight + Grid.GridLineWidth);
      for I := 0 to Grid.ControlCount - 1 do
      begin
        Grid.Controls[I].Top := Grid.Controls[I].Top - Shift;
        Grid.Controls[I].Visible := Grid.Controls[I].Top > 0;
      end;
    end;
    
    function TForm1.GetInsertRowCount(ARow: Integer): Integer;
    begin
      //This function should return the number of rows which is to be inserted
      //below ARow. Note that ARow refers to the original row index, that is:
      //without account for already inserted rows. For now, assume three rows:
      Result := 3;
    end;
    
    function TForm1.GridButtonToRow(AButton: TButton): Integer;
    begin
      for Result := 0 to Grid.RowCount - 1 do
        if Grid.Objects[0, Result] = AButton then
          Exit;
      Result := -1;
    end;
    
    procedure TForm1.MoveGridButtons(ButtonIndex, ARowCount: Integer);
    var
      I: Integer;
    begin
      for I := 0 to Grid.ControlCount - 1 do
        if Grid.Controls[I].Tag > ButtonIndex then
          Grid.Controls[I].Top := Grid.Controls[I].Top +
            ARowCount * (Grid.DefaultRowHeight + Grid.GridLineWidth);
    end;
    
    end.