8

I have a TDBGrid. It works, but the columns shown are very large.

How can I set an "auto-fix column width"?

EMBarbosa
  • 1,462
  • 1
  • 23
  • 68
ramiromd
  • 1,918
  • 7
  • 29
  • 57
  • 4
    The width of each column is per default adjusted to the declared size of the corresponding field. If this doesn't match reality you should rethink your database design. – Uwe Raabe Jul 07 '13 at 07:33
  • 1
    @Uwe, that's true for field's `DisplayWidth` count of `0` chars, which is 6px wide each with Tahoma size 8 font (at default scaling). If you were displaying, let's say a 100 of `W` chars (which is 10px wide each), the default column width would be 400px narrower than is actually needed (when the `DisplayWidth` would be 100). So there might be also an opposite reason to autosize the widths. – TLama Jul 07 '13 at 17:33

6 Answers6

11

The needed Columnwidth is depended of the settings of the Grids canvas and the mamimum length of the displaytext of each field.

procedure FitGrid(Grid: TDBGrid);
const
  C_Add=3;
var
  ds: TDataSet;
  bm: TBookmark;
  i: Integer;
  w: Integer;
  a: Array of Integer;
begin
  ds := Grid.DataSource.DataSet;
  if Assigned(ds) then
  begin
    ds.DisableControls;
    bm := ds.GetBookmark;
    try
      ds.First;
      SetLength(a, Grid.Columns.Count);
      while not ds.Eof do
      begin
        for I := 0 to Grid.Columns.Count - 1 do
        begin
          if Assigned(Grid.Columns[i].Field) then
          begin
            w :=  Grid.Canvas.TextWidth(ds.FieldByName(Grid.Columns[i].Field.FieldName).DisplayText);
            if a[i] < w  then
               a[i] := w ;
          end;
        end;
        ds.Next;
      end;
      for I := 0 to Grid.Columns.Count - 1 do
        Grid.Columns[i].Width := a[i] + C_Add;
        ds.GotoBookmark(bm);
    finally
      ds.FreeBookmark(bm);
      ds.EnableControls;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FitGrid(DBgrid1)
end;
bummi
  • 26,435
  • 13
  • 58
  • 97
  • 6
    ZeroMemory is not needed. SetLength initializes all new members with zero. – Uwe Raabe Jul 07 '13 at 07:26
  • Good solution. Just a nit pick: in the inner `if a[i] < w then` I'd set `a[i] := w;` then set `Grid.Columns[i].Width := a[i] + C_Add` in the last `for` loop. – lurker Jul 07 '13 at 18:30
  • Which can take a long time on a large data set, and barfs when you have a stray record with a very long content. Which incidentally are the reasons the `TDBGrid` does not have this kind of functionality in the first place, and Windows Explorer does not resize 'properly' under some circumstances either. – Jeroen Wiert Pluimers Jul 08 '13 at 06:27
4

Minor modification of bummi's answer to insure that Title Row (Row 0) is not truncated, and excess space will be allocated on each column

procedure FitGrid(Grid: TDBGrid);
const
  C_Add = 3;
var
  ds: TDataSet;
  bm: TBookmark;
  i: Integer;
  w: Integer;
  a: array of Integer;
begin
  ds := Grid.DataSource.DataSet;

  if not Assigned(ds) then
    exit;

  if Grid.Columns.Count = 0 then
    exit;

  ds.DisableControls;
  bm := ds.GetBookmark;
  try
    ds.First;
    SetLength(a, Grid.Columns.Count);
    for i := 0 to Grid.Columns.Count - 1 do
      if Assigned(Grid.Columns[i].Field) then
        a[i] := Grid.Canvas.TextWidth(Grid.Columns[i].FieldName);

    while not ds.Eof do
    begin

      for i := 0 to Grid.Columns.Count - 1 do
      begin
        if not Assigned(Grid.Columns[i].Field) then
          continue;

        w := Grid.Canvas.TextWidth(ds.FieldByName(Grid.Columns[i].Field.FieldName).DisplayText);

        if a[i] < w then
          a[i] := w;
      end;
      ds.Next;
    end;

    w := 0;
    for i := 0 to Grid.Columns.Count - 1 do
    begin
      Grid.Columns[i].Width := a[i] + C_Add;
      inc(w, a[i] + C_Add);
    end;

    w := (Grid.ClientWidth - w - 20) div (Grid.Columns.Count);

    if w > 0 then
      for i := 0 to Grid.Columns.Count - 1 do
        Grid.Columns[i].Width := Grid.Columns[i].Width + w;


    ds.GotoBookmark(bm);
  finally
    ds.FreeBookmark(bm);
    ds.EnableControls;
  end;
end;
Jens Borrisholt
  • 5,641
  • 1
  • 25
  • 56
3

Minor modification of bummi's answer to insure that Title Row (Row 0) is not truncated

procedure FitGrid(Grid: TDBGrid);
const
  C_Add=3;
var
  ds: TDataSet;
  bm: TBookmark;
  i: Integer;
  w: Integer;
  a: Array of Integer;
begin
  ds := Grid.DataSource.DataSet;
  if Assigned(ds) then
  begin
    ds.DisableControls;
    bm := ds.GetBookmark;
    try
      ds.First;
      SetLength(a, Grid.Columns.Count);
      while not ds.Eof do
      begin
        for I := 0 to Grid.Columns.Count - 1 do
        begin
          if Assigned(Grid.Columns[i].Field) then
          begin
            w :=  Grid.Canvas.TextWidth(ds.FieldByName(Grid.Columns[i].Field.FieldName.).DisplayText);
            if a[i] < w  then
               a[i] := w ;
          end;
        end;
        ds.Next;
      end;
      //if fieldwidth is smaller than Row 0 (field names) fix
      for I := 0 to Grid.Columns.Count - 1 do
      begin
        w :=  Grid.Canvas.TextWidth(Grid.Columns[i].Field.FieldName);
        if a[i] < w  then
           a[i] := w ;
      end;

      for I := 0 to Grid.Columns.Count - 1 do
        Grid.Columns[i].Width := a[i] + C_Add;
        ds.GotoBookmark(bm);
    finally
      ds.FreeBookmark(bm);
      ds.EnableControls;
    end;
  end;
end;
TheSteven
  • 860
  • 8
  • 22
  • Hi, I am pretty new to delphi and I would like to ask you, to explain how is this working in detail. What are variables like i, w and a good for, ect. I will be really thankful. – user2886091 Mar 26 '14 at 11:28
1

Minor modification of bummi's, TheSteven's and Jens' answers to insure that Title Row (Row 0) is not truncated, and excess space will be allocated on each column, and take visibility of columns into account.

procedure FitGrid(const Grid: TDBGrid; const CoverWhiteSpace: Boolean = True);
const
  C_Add=3;
var
  DS: TDataSet;
  BM: TBookmark;
  I, W, VisibleColumnsCount: Integer;
  A: array of Integer;
  VisibleColumns: array of TColumn;
begin
  DS := Grid.DataSource.DataSet;
  if Assigned(DS) then
  begin
    VisibleColumnsCount := 0;
    SetLength(VisibleColumns, Grid.Columns.Count);
    for I := 0 to Grid.Columns.Count - 1 do
      if Assigned(Grid.Columns[I].Field) and (Grid.Columns[I].Visible) then
      begin
        VisibleColumns[VisibleColumnsCount] := Grid.Columns[I];
        Inc(VisibleColumnsCount);
      end;
    SetLength(VisibleColumns, VisibleColumnsCount);

    DS.DisableControls;
    BM := DS.GetBookmark;
    try
      DS.First;
      SetLength(A, VisibleColumnsCount);
      while not DS.Eof do
      begin
        for I := 0 to VisibleColumnsCount - 1 do
        begin
            W :=  Grid.Canvas.TextWidth(DS.FieldByName(VisibleColumns[I].Field.FieldName).DisplayText);
            if A[I] < W then
               A[I] := W;
        end;
        DS.Next;
      end;
      //if fieldwidth is smaller than Row 0 (field names) fix
      for I := 0 to VisibleColumnsCount - 1 do
      begin
        W := Grid.Canvas.TextWidth(VisibleColumns[I].Field.FieldName);
        if A[I] < W then
           A[I] := W;
      end;

      W := 0;
      if CoverWhiteSpace then
      begin
        for I := 0 to VisibleColumnsCount - 1 do
          Inc(W, A[I] + C_Add);
        W := (Grid.ClientWidth - W - 20) div VisibleColumnsCount;
        if W < 0 then
          W := 0;
      end;

      for I := 0 to VisibleColumnsCount - 1 do
        VisibleColumns[I].Width := A[I] + C_Add + W;
      DS.GotoBookmark(BM);
    finally
      DS.FreeBookmark(BM);
      DS.EnableControls;
    end;
  end;
end;
saastn
  • 4,665
  • 3
  • 26
  • 46
0

I found 'Columns' in the properties of my TDBGrid and new window is opened. In that window I add new FieldNames - you can choose columnNames only from the result of your SQL string of your TADOQuery and then when you click to the exact column you can find 'Width' in the properties of selected column so change it whatever you like. It works for me.

0

I'm afraid the last adaptation of the code is no correct.

I would rather write something like :

 const
  C_Add=20;

//.....

Sup := 0;
      if CoverWhiteSpace then
        sup :=C_Add;

      for I := 0 to VisibleColumnsCount - 1 do
          VisibleColumns[I].Width := A[I] + sup;
      DS.GotoBookmark(BM);