如何根据内容自动调整/缩放DBGrid(或其他类似)的列宽?

10

我正在尝试制作一个带有DBGrid的框架,可用于超过10个表格,其中一半字段是默认值,另一半字段是每个表格独有的。

由于列的空间有限,我不想手动配置每个表格的每一列,因为这是非常低质量的工作,所以我在想办法通过该列内最大行的内容计算每列的宽度,由组件本身或数据集进行测量。

有人知道吗?世界上是否有一些具有此功能的自定义组件? 我需要一种解决方案,根据网格中所有可见列的可见数据实现大小的增加和减小。 到目前为止,我的解决方案存在选定单元格的绘制问题,导致其跳出选定的数据集行。

enter image description here


注意:请不要关闭我的问题。这不是关于适合网格宽度或表单宽度的问题。这涉及到所有列的宽度都要最小化水平滚动条,但不一定要隐藏水平滚动条。


@RRUZ,它不适合表格的格式,但是在其自身内容中,即使水平滚动条可见。 - NaN
我知道我以前问过这个问题,并得到了一个非常好的答案... - Jerry Dodge
@kobik 这个链接已经失效了。 - SHIN JaeGuk
8个回答

11
你需要做的是使用网格画布来测量每列的内容并相应地设置列宽。你可以遍历数据集或使用OnColumnDraw事件动态调整宽度。 这里是一个示例(我不得不使用5像素的偏移量)。
procedure TForm7.DBGridDrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
Var
  w : Integer;

begin
  w := 5+DBGrid.Canvas.TextExtent(Column.Field.DisplayText).cx;
  if w>column.Width then Column.Width := w;
end;

procedure TForm7.FormActivate(Sender: TObject);
Var
  i : Integer;

begin
  // Initialize width
  for I := 0 to DBGrid.Columns.Count - 1 do
    DBGrid.Columns[i].Width := 5 + DBGrid.Canvas.TextWidth(DBGrid.Columns[i].title.caption)
end;

这个答案非常明显,我是多么的愚蠢啊?非常感谢! - NaN
我将使用一个标记来检查最大宽度,以便在需要时缩小列宽。 - NaN
嗯,我的答案最近被“未被接受”。我想知道为什么。 - alzaimar
我也遇到了这个问题,但我使用的是C++ Builder XE而不是Delphi。我尝试手动将此代码转换为BCBXE语言,但我不理解这一行: w:= 5 + DBGrid.Canvas.TextExtent(Column.Field.DisplayText).cx; 你能告诉我如何在C++ Builder XE中实现吗? 提前致谢。 - Jessie M
抱歉,我不使用C++。但这应该很明显。 - alzaimar
显示剩余2条评论

8

编辑:

我的第一段代码是关于将列适应网格宽度的,而这段新代码中,AutoSizeColumns会读取记录以计算每个列的宽度,直到达到MaxRows或Dataset.Eof为止:

class function TDBGridHelper.AutoSizeColumns(DBGrid: TDBGrid; const MaxRows: Integer = 25): Integer;

var
  DataSet: TDataSet;
  Bookmark: TBookmark;
  Count, I: Integer;
  ColumnsWidth: array of Integer;
begin
  SetLength(ColumnsWidth, DBGrid.Columns.Count);
  for I := 0 to DBGrid.Columns.Count - 1 do
    if DBGrid.Columns[I].Visible then
      ColumnsWidth[I] := DBGrid.Canvas.TextWidth(DBGrid.Columns[I].Title.Caption + '   ')
    else
      ColumnsWidth[I] := 0;
  if DBGrid.DataSource <> nil then
    DataSet := DBGrid.DataSource.DataSet
  else
    DataSet := nil;
  if (DataSet <> nil) and DataSet.Active then
  begin
    Bookmark := DataSet.GetBookmark;
    DataSet.DisableControls;
    try
      Count := 0;
      DataSet.First;
      while not DataSet.Eof and (Count < MaxRows) do
      begin
        for I := 0 to DBGrid.Columns.Count - 1 do
          if DBGrid.Columns[I].Visible then
            ColumnsWidth[I] := Max(ColumnsWidth[I], DBGrid.Canvas.TextWidth(
              DBGrid.Columns[I].Field.Text));
        Inc(Count);
        DataSet.Next;
      end;
    finally
      DataSet.GotoBookmark(Bookmark);
      DataSet.FreeBookmark(Bookmark);
      DataSet.EnableControls;
    end;
  end;
  Count := 0;
  for I := 0 to DBGrid.Columns.Count - 1 do
    if DBGrid.Columns[I].Visible then
    begin
      DBGrid.Columns[I].Width := ColumnsWidth[I];
      Inc(Count, ColumnsWidth[I]);
    end;
  Result := Count - DBGrid.ClientWidth;
end;

我会在 DataSet.AfterOpen 事件中调用它:
TGridHelper.AutoSizeColumns(MyDBGrid);

这不是问题的答案。 - NGLN
2
否定是不公平的,因为原帖作者没有先解释他真正想要的是什么,现在我编辑了我的帖子,以满足同一单元的另一个代码的需求。 - Cesar Romero
@NGLN,这个踩票并不公平。我的答案是基于原始帖子的,当OP编辑并更好地解释了真正的需求后,我更新了我的帖子。 - Cesar Romero
1
+1;非常好的答案,我在实现类似功能后找到了它。小差异是,默认情况下我使用DbGrid的RowCount来限制要检查的行数(因为这些记录已经从数据库中获取),除非是TClientDataSet,我会扫描所有记录(它们都在内存中)。 - Jeroen Wiert Pluimers
只是一个类助手不能像这样被调用。 - RBA
显示剩余4条评论

2
为什么要使用那么复杂的代码呢? :D 只需要使用这个。它还有5像素的偏移量。
procedure TForm1.FormActivate(Sender: TObject);

var

  i:integer;

begin

  for i :=0 to DbGrid1.Columns.Count - 1 do
   DbGrid1.Columns[i].width :=5+dbgrid1.Canvas.TextWidth(DbGrid1.Columns[i].Title.Caption);

end;

1
对于每一列您想要自动调整大小的,请设置属性SizePriority=1,对于固定列,请设置SizePriority=0,在您的情况下只有最后一列将自动调整大小。
然后设置属性grid.AutoFillColumns=true,就可以了。

1
对于 TRUEDBGRID .net,您可以这样做:

 Private Sub AutoSizeGrid(Grid As C1.Win.C1TrueDBGrid.C1TrueDBGrid)
        For Each Sp As C1.Win.C1TrueDBGrid.Split In Grid.Splits
            For Each Cl As C1.Win.C1TrueDBGrid.C1DisplayColumn In Sp.DisplayColumns
                Cl.AutoSize()
            Next
        Next
End Sub

对于vb60中的TrueDbGrid ActiveX,以下是代码(此代码不包括分割):
Public Function APEXGridAutoFix(Grid As TrueOleDBGrid80.TDBGrid)
Dim Col As TrueOleDBGrid80.Column
For Each Col In Grid.Columns
    Col.AutoSize
Next
End Function

0

这个解决方案可以根据内容使所有列扩展或收缩,而不必关心是否需要滚动条,并修复选定单元格绘图故障和记录指针故障。

type
  TColumnAutoAdjust = record {Save the information responsible for setting column widths in the grid}
    Field: String;           {Field name whose information is being stored}
    Registered: Boolean;     {Indicates whether the size of this column already registered}
    Updated: Boolean;        {Indicates the actual size of the column was updated}
    LastWidth: Integer;      {Width indicates the final text of a record of a row column}
    CurrWidth: Integer;      {Indicates the current size and column width}
    Reverter: Integer;       {Indicates the greatest width recorded but that is less than the current}
    Scrolls: Integer;        {Indicates the amount of scrolls present after one width adjustment}
    RecNo: Integer;          {Indicates which was the record in the table which increased the width of colune}
  end;

var { inside the forms private }
  gdCols: array of TColumnAutoAdjust; { vetor de ajuste de largura de cada coluna na grade de resultado }
  RegisteredCols: Integer; { quantas colunas já foram registradas no controle de ajuste }
  gdVisibleRows: Integer; { quantas linhas de cadastros estão visíveis da grade de resultado }
  gdVisibleCols: Integer; { quantas colunas de cadastros estão visíveis da grade de resultado }

{ before showing the grid }  
    RegisteredCols := ResultGrid.Columns.Count;
    SetLength(gdCols, RegisteredCols); { determina o tamanho da vetor de controle de colunas }
    { libera a lista }
    ResultGrid.Align := alClient;
    for i := 0 to RegisteredCols -1 do { inicializando a largura das colunas no tamanho do título de cada }
    begin
      gdCols[i].Field := ResultGrid.Columns[i].FieldName;
      ResultGrid.Columns[i].Width := ResultGrid.Canvas.TextExtent(ResultGrid.Columns[i].Title.Caption).cx;
      ResultGrid.Columns[i].Alignment := taLeftJustify;
      ResultGrid.Columns[i].Title.Alignment := taLeftJustify;
    end;
    BrowserQuery.Open;
    ResultGrid.Show;
    for i := 0 to gdVisibleRows do
    begin
      BrowserQuery.Next;
      ResultGrid.Refresh;
    end;
    for i := 0 to gdVisibleRows do
    begin
      BrowserQuery.Prior;
      ResultGrid.Refresh;
    end;
    BrowserQuery.First;
    ResultGrid.SetFocus;
  end

{ after dataset scroll}      
procedure TRecordsBrowserFrameBase.BrowserQueryAfterScroll(DataSet: TDataSet);
var
  i, TitleWidth: Integer;
  mayAdjustAgain: Boolean; {  }
begin
{ ajusta as colunas da grade de resultado a cada movimento da tabela de resultado }
  mayAdjustAgain := False;
  for i := 0 to RegisteredCols -1 do
  begin
    if not gdCols[i].Updated then
    begin
      ResultGrid.Columns[i].Width := gdCols[i].CurrWidth;
      gdCols[i].Scrolls := 0;
      gdCols[i].Updated := True;
    end
    else
    begin
      Inc(gdCols[i].Scrolls);
      if (DataSet.RecNo > gdCols[i].RecNo + gdVisibleRows) or (DataSet.RecNo < gdCols[i].RecNo - gdVisibleRows) then
      begin
        TitleWidth := MaxColSpacing + ResultGrid.Canvas.TextExtent(ResultGrid.Columns[i].Title.Caption).cx;
        gdCols[i].LastWidth := gdCols[i].CurrWidth;
        gdCols[i].CurrWidth := IFX(gdCols[i].Reverter > TitleWidth, gdCols[i].Reverter, TitleWidth);
        gdCols[i].Reverter := IFX(gdCols[i].Reverter > TitleWidth, TitleWidth, 0);
        gdCols[i].Updated := False;
        mayAdjustAgain := True;
      end;
    end;
  end;
  if mayAdjustAgain then
  begin
    ResultGrid.Refresh;
    BrowserQueryAfterScroll(DataSet);
  end;
end;

{ on draw column cell }

procedure TRecordsBrowserFrameBase.GridColumnWidthAdjust(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  ColWidth, TextWidth, TitleWidth: Integer;
begin
{ ajusta a capitalização do texto das células }
  (Sender as TJvDBGrid).Canvas.Pen.Color := clWhite;
  (Sender as TJvDBGrid).Canvas.Rectangle(Rect);
  (Sender as TJvDBGrid).Canvas.TextOut(Rect.Left+2, Rect.Top+2, NameCase(Column.Field.DisplayText));
{ ajusta as colunas de uma grade de acordo com o conteúdo das células }
  gdVisibleRows := (Sender as TJvDBGrid).VisibleRowCount;
  gdVisibleCols := (Sender as TJvDBGrid).VisibleColCount;
  TitleWidth := MaxColSpacing + (Sender as TJvDBGrid).Canvas.TextExtent(Column.Title.Caption).cx;
  TextWidth := MaxColSpacing + (Sender as TJvDBGrid).Canvas.TextExtent(NameCase(Column.Field.DisplayText)).cx;
  ColWidth := Column.Width;
  {$WARNINGS OFF}
  if (TextWidth > gdCols[DataCol].Reverter) and (TextWidth < ColWidth) then gdCols[DataCol].Reverter := TextWidth;
  if (TextWidth > ColWidth) then { texto da célula é mais largo que a coluna }
  begin
    gdCols[DataCol].Registered := True;
    gdCols[DataCol].LastWidth := ColWidth;
    gdCols[DataCol].CurrWidth := TextWidth;
    gdCols[DataCol].Updated := False;
    gdCols[DataCol].RecNo := BrowserQuery.RecNo;
    gdCols[DataCol].Reverter := TitleWidth;
    Exit;
  end;
  if (ColWidth < TitleWidth) then { texto da célula é menor que o título da coluna }
  begin
    gdCols[DataCol].Registered := True;
    gdCols[DataCol].LastWidth := ColWidth;
    gdCols[DataCol].CurrWidth := TitleWidth;
    gdCols[DataCol].Updated := False;
    gdCols[DataCol].Reverter := TitleWidth;
    Exit;
  end;
{$WARNINGS ON}
end;

-1

你好,请使用这个过程。

Procedure AutoSizeColDBGrid(DBGrid:TDBGrid);
var i, ColWidth, ColTextWidth:integer;
begin
 if DBGrid.DataSource.DataSet.Active then
   begin
     DBGrid.DataSource.DataSet.DisableControls;
     for i:= 0 to DBGrid.Columns.Count-1 do
       begin
         ColWidth:=DBGrid.Canvas.TextWidth(DBGrid.Columns[i].Field.DisplayLabel);
         DBGrid.DataSource.DataSet.First;
       while not DBGrid.DataSource.DataSet.EOF do
        begin
       ColTextWidth:=DBGrid.Canvas.TextWidth(DBGrid.Columns[i].Field.DisplayText);
          if (ColTextWidth > ColWidth) then
            begin
              ColWidth := ColTextWidth;
            end;
          DBGrid.DataSource.DataSet.Next;
       end;{while}
       DBGrid.Columns[i].Width:=ColWidth+10;
     end;{for}
DBGrid.DataSource.DataSet.EnableControls;
DBGrid.DataSource.DataSet.First;
end;
end;

-2
使用过程 DbGrid1.AutoAdjustColumns 就可以了。

1
这是一个新的过程还是在旧版本的Delphi中已经存在了? - NaN
在最新版本的Delphi中,没有这样的方法。 - M.J.Ahmadi
在Embacadero开发者社区上发布了一篇程序-请参见上文。 - Michél Picker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接