Link to home
Start Free TrialLog in
Avatar of boardtc
boardtcFlag for Ireland

asked on

Autosize TDbGrid columns

Hi, I am running D C/S 4.03. I am wondering if there is a way to autosize the columns in a TDbGrid, I would like the initial size to be as wide as the column name or the largest data in the column. Any suggestions would be appreciated.

Thanks, Tom.
Avatar of kretzschmar
kretzschmar
Flag of Germany image

? does the dbgrid not allways autosize the columns ?
Avatar of Radler
Radler

I hate my proper comments, because they aren't the complete solution. Well, let's go:
All steps will done at OnDrawcell
1 - Measure the length necessary to draw the field content without clipping
1.1 - Use TCanvas.TextExtent( field.Content.AsString ...).Cx
2 - Set the Grid.Fields[?].DisplayWidth with this value;
3 - Call Grid.Update

T++, Radler.
Avatar of boardtc

ASKER

Radler, thanks, Why would I resize the column every time in OnDrawCell?

Thanks, Tom.
Only a part of all records are loaded by moment. So supposing that the first 40 records have a length 100 and the rest 120 a trunc would be done.

T++, Radler.
tomcorcoran :

My way is not a good idea.  :-)


var
  maxl:array [0..10]of integer;

implementation

{$R *.DFM}

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  maxl[Column.Index]:=max(maxl[Column.Index],max(length(trim(Column.Field.asstring))*DBGrid1.TitleFont.size,length(column.field.DisplayName)*DBGrid1.Font.size));
  Column.Width:=trunc(maxl[Column.Index]);
end;

menxin
Avatar of boardtc

ASKER

Thnaks guys. I was hoping there was a more elegant solution. Anyone know one?

Tom.
Elegant solution ?
Well, let's go:
Supposing a TStringField, to others field types, like Numeric/date the size is what you desire

var
Len : word;
Str : string;
begin
Len:=TStringField( Grid.Fields[?] ).DataSize;
FillChar( Str, Len, 'X' ); //A large char
Len:=Grid.Canvas.TextExtent( Str ). Cx
Grid.Fields[?].DisplayWidth:=Len;
end;

Put this code segment at a location before user view the data, like OnShow by Example.
It's the more optmizated solution.

T++, Radler.
Avatar of boardtc

ASKER

Radler,

Sorry for the holiday enforced delay. I tried your suggestion in OnDrawColumnCell and got an AV on the line:

Len:=Grid.Canvas.TextExtent( Str ).Cx

I couldn't figure any more info.

Cheers, Tom.
 
Ok a AV( Access Violation can appear ).
Tom, but the "elegant" solution ?
Was tried ?
Put this code at OnShow OK ?

T++, Radler.
Avatar of boardtc

ASKER

I tried the elegant soilution in OnDrawColumnCell. In OnShow, are you suggesting a for loop on the fieldcount to acecss the fields array? Anyway I tried that and get a AV in the same place. Thanks, Tom.
Seems that I need make a sample code to you. My time is short now, at next week I'm more availble. Wait a bit, sorry, is my astral hell.

T++, Radler.
Avatar of boardtc

ASKER

Thanks, I'm afraid your solution did not work:

procedure Tfrm.FormShow(Sender: TObject);
var
  nLen, nIndex : word;
  sTemp : string;
begin
  for nIndex := 0 to dbIndex.FieldCount - 1 do
  begin
    nLen := TStringField(dbIndex.Fields[nIndex]).DataSize;
    FillChar(sTemp, nLen, 'X' ); //A large char
    nLen := dbIndex.Canvas.TextExtent(sTemp).Cx;
    dbIndex.Fields[nIndex].DisplayWidth := nLen;
  end;
end;

Thanks, Tom.
Hi, I back soon.
I tested this and is OK. The visual result will depend of fields length and maybe very large at some situations.


procedure TForm1.FormShow(Sender: TObject);
var
  nLen, nIndex : word;
  sTemp : string;
begin
      for nIndex := 0 to Table1.FieldCount-1 do begin
            nLen := TStringField(Table1.Fields[nIndex]).DataSize;
            SetLength( sTemp, nLen );
            sTemp:=StringOfChar('X', nLen);
            nLen := DBGrid1.Canvas.TextExtent(sTemp).Cx;
            DBGrid1.Fields[nIndex].DisplayWidth := nLen;
      end;
end;

T++, Radler
Avatar of boardtc

ASKER

Radler,

Makes sense, SetLength instead of FillChar. It does not crash now, but results in masively wide columns, for instance the first one is wider than my screen. What happens is that nLen is 31 but

nLen := DBGrid1.Canvas.TextExtent(sTemp).Cx;

converts it to 217.

Thanks and cheers, Tom.
Tom, was what I said. You need calc the column length at dispaly time to avoid larger columns. Or smaller fields datasizes comparing with the real data stored.
Well supposing that you designed your DB the choice is your. I prefer at OnDraw? events.
An alternative is OnTitleClick ( more user interface elegant ).
Have a nice work/day.

T++, Radler.
If you remember you can calculate the length for the exibited records
DefaultDrawing:=False; //first

Table.DisableControls //second

sTemp:=Table1.Fields[nIndex].AsString;
nLen := DBGrid1.Canvas.TextExtent(sTemp).Cx; //third

Table.EnableControls;

DbGrid.Invalidate; //Optional.

Not tested, waiting for reply.

T++, Radler.
Avatar of boardtc

ASKER

Radler, thank you. However, I am totally lost as to what you are saying but I will play around with setting DefaultDrawing.
Cheers, Tom.
I use the following to auto size all my grids in app, not real flash but does the job.

Procedure TdmData.AutoSizeGrid(Grid : TDBGrid; Query : TQuery;
                               HideFirst, HideLast : boolean);
var
  c,r : integer;
  Multiplier : integer;
begin
  for c := 0 to Grid.Columns.Count - 1
  do
  begin
    Grid.columns[c].width := 50;  //minimum
    if 50 < length(query.Fields[c].FieldName) * 7
    then
      Grid.Columns[c].width := length(query.Fields[c].fieldname) * 7;
  end;
  query.First;
  for r := 0 to query.recordCount - 1
  do
  begin
    for c := 0 to Grid.Columns.Count - 1
    do
    begin
      if frmSearch.getfieldtype(query.fielddefs[c].datatype) = 0
      then
        Multiplier := 12
      else
        Multiplier := 7;
      if Grid.Columns[c].width < length(query.Fields[c].asstring) * Multiplier
      then
        Grid.Columns[c].width := length(query.Fields[c].asstring) * Multiplier;
    end;

    query.next;

  end;

  query.first;

  if HideFirst
  then
    Grid.Columns[0].width := 0;

  if HideLast
  then
    Grid.Columns[Grid.Columns.Count - 1].width := 0;

end;


Regards
Darren
Avatar of boardtc

ASKER

Radler,

It was a good attempt. Thank you. Please answer and I will grade,

Tom.
ASKER CERTIFIED SOLUTION
Avatar of Radler
Radler

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of boardtc

ASKER

cheers rad man
Thanks Tom.
The dhnkley's code give a idea now. At future you can put a call to my code only to response a user click at column header to resize the next/previous viewed records.
Thanks again.

T++, Radler.