Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi DBGrid1.Columns[0].Width:= .... / geht das auch automatisch? (https://www.delphipraxis.net/209357-dbgrid1-columns%5B0%5D-width-%3D-geht-das-auch-automatisch.html)

NicoleWagner 25. Nov 2021 17:31

DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Mühsam lasse ich mein Programm immer wieder laufen und schreibe dann müüüühsam für jede Spalte extra die Breite fest:
DBGrid1.Columns[0].Width:=60;
DBGrid1.Columns[1].Width:=20;
DBGrid1.Columns[2].Width:=80;
usw. usw. usw.

Gibt es einen Weg, der die Spaltenbreite autoamtisch individuell der jeweiligen Textlänge anpasst?

Delphi.Narium 25. Nov 2021 17:57

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Ja, das geht, ungetestet ungefähr sowas:
Delphi-Quellcode:
// Im AfterOpen der Query, Table, ...
procedure TForm1.DatenbankomponentennameAfterOpen(DataSet: TDataSet);
var
  lb : TLabel;
  i : Integer;
begin
  lb := TLabel.Create(Self);
  lb.AutoSize := true;
  lb.ParentFont := true;
  // Wenn das DBGrid 'ne andere Schriftgröße hat, als das Formular:
  // lb.ParentFont := false;
  // lb.Font := DBGrid1.Font;
  // Damit werden die Spaltenbreiten an den Inhalt der ersten Zeile der Datenmenge angepasst.
  for i := 0 to DBGrid1.Columns.Count - 1 do begin
    lb.Caption := DBGrid1.Columns[i].Field.AsString;
    DBGrid1.Columns[i].Width := lb.Width;
  end;
  lb.Free;
end;
Alternative: Das DBGrid aus der JVCL nehmen, das bringt sowas und viele Vereinfachungen von Hause aus mit.

Der schöne Günther 26. Nov 2021 08:22

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Nur als Hinweis: Die Breite der Spalten kannst du auch bereits im Formular-Designer festlegen.

TiGü 26. Nov 2021 09:02

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Wäre das nicht einfacher über den Canvas zu lösen?

Delphi-Quellcode:
// Im AfterOpen der Query, Table, ...
procedure TForm1.DatenbankomponentennameAfterOpen(DataSet: TDataSet);
var
  i : Integer;
  LColumn: TColumn;
begin
  for i := 0 to DBGrid1.Columns.Count - 1 do
  begin
    LColumn := DBGrid1.Columns[i];
    LColumn.Width := DBGrid1.Canvas.TextWidth(LColumn.Field.AsString);
  end;
end;

Delphi.Narium 26. Nov 2021 10:15

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
@TiGü

jo, eindeutig ;-)
Soweit hab' ich halt mal wieder nicht gedacht.
Vorteil Deiner Version: Man muss sich nichtmal drum kümmern, ob die Schriftgröße, ... irgendwie passen.

Aber dann machen wir es noch ein bisserl kürzer ;-)
Delphi-Quellcode:
  for i := 0 to DBGrid1.Columns.Count - 1 do
  begin
    DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(DBGrid1.Columns[i].Field.AsString);
  end;

Uwe Raabe 26. Nov 2021 10:40

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Zitat:

Zitat von NicoleWagner (Beitrag 1498208)
Gibt es einen Weg, der die Spaltenbreite autoamtisch individuell der jeweiligen Textlänge anpasst?

Das Problem dabei ist, dass du das für jeden Datensatz machen und dann jeweils für jede Spalte den Maximalwert ermitteln müsstest. Das kann unter gewissen Bedingungen schon mal recht unperformant werden.

NicoleWagner 26. Nov 2021 11:16

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für alle Antworten!

Nur jetzt bekomme ich Kopfschmerzen:
Leider funktionieren in meinem Fall beide Methoden nicht, auch wenn das Ergebnis gleich aussieht.
Siehe Screenshot.

Spannend ist die Spalte PL:
Sie ist überbreit. Sie WAR auch überbreit in der Query.
Denn es handelt sich um eine double Zahl, deren zahlreiche Kommastellen ich im Draw-Event auf 2 reduziere.
d.h. vielleicht ist die Spaltenbreite-Routine in einem Event, das zu früh ausgelöst wird.

nur, meine Kopfschmerzen:
Die Abfrage (siehe andere meiner Fragen)
i:=DataSource_TradesListen.DataSet.Fields.Count - 1;
wird erstmals korrekt bearbeitet. Und zwar im Draw Event!
Die Linie, die die Monate trennt, ist erstmal genau, wo sie sein soll.


Ich glaube, ich brauche eine Pause, weil nur mehr verwirrt.

Delphi.Narium 26. Nov 2021 11:44

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Naja, dann machen wir mal weiter und ergänzen die Routine:
Delphi-Quellcode:
  for i := 0 to DBGrid1.Columns.Count - 1 do
  begin
    case DBGrid1.Columns[i].Field.DataType of
      ftString, ftWideString :
      begin
        DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(DBGrid1.Columns[i].Field.AsString);
        DBGrid1.Columns[i].Alignment := taLeftJustify;
      end;
      ftSmallint, ftInteger, ftWord, ftAutoInc, ftLargeint :
      begin
        DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(Format('%6.0n',[DBGrid1.Columns[i].Field.AsFloat]));
        DBGrid1.Columns[i].Alignment := taRightJustify;
      end;
      ftFloat, ftCurrency :
      begin
        DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(Format('%6.2n',[DBGrid1.Columns[i].Field.AsFloat]));
        DBGrid1.Columns[i].Alignment := taRightJustify;
      end;
      ftDate :
      begin
        DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(FormatDateTime('dd.mm:yyyy', DBGrid1.Columns[i].Field.AsDateTime]));
        DBGrid1.Columns[i].Alignment := taLeftJustify;
      end;
      ftTime :
      begin
        DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(FormatDateTime('hh:nn:ss', DBGrid1.Columns[i].Field.AsDateTime]));
        DBGrid1.Columns[i].Alignment := taLeftJustify;
      end;
      ftDateTime :
      begin
        DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(FormatDateTime('dd.mm.yyyy hh:nn:ss', DBGrid1.Columns[i].Field.AsDateTime]));  
        DBGrid1.Columns[i].Alignment := taLeftJustify;
      end;
    else
      DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(DBGrid1.Columns[i].Field.AsString);  
      DBGrid1.Columns[i].Alignment := taCenter;
    end;
  end;
Achso: Das ist jetzt einfach nur ungetestet hingedaddelt.
Bei den Format- bzw. FormatDateTimeaufrufen kannst Du die Formatstrings ja entsprechend Deinen Wünschen anpassen, ebenso die rechts- oder linksbündige Ausgabe ...

joachimd 26. Nov 2021 12:43

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
ich würde jetzt nicht unbedingt die Breite ermitteln, sondern nur eine maximale Breite setzen...
Code:
// Im AfterOpen der Query, Table, ...
procedure TForm1.DatenbankomponentennameAfterOpen(DataSet: TDataSet);
var
  LColumn: TColumn;
begin
  for LColumn in DBGrid1.Columns do
    LColumn.Width := min(LColumn.Width, 30);
end;

Jumpy 26. Nov 2021 12:43

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Noch eine Version, die ich meine ich irgendwann mal hier im Forum bekommen habe (find es gerade nicht), ich meine von Sir Rufo. Hier in der Version analysiert es die ersten 40 Zeilen, um die optimale Spaltenbreite zu ermitteln:

Delphi-Quellcode:
procedure TProjekt.SetGridCol_Optimal(DBGrid: TDBGrid);
function TextGroesse(const Text : string; Font : TFont = nil) : TSize;
var
  DC : hDC;
  F : hFont;
begin
  F := 0;
  DC := GetDC(0);
  try
    if Font <> nil then F := SelectObject(DC, Font.Handle);
    if not GetTextExtentPoint32(DC, PChar(Text), Length(Text), Result) then
    begin
      Result.cx := 0;
      Result.cy := 0;
    end;
  finally
    if F <> 0 then SelectObject(DC, F);
    ReleaseDC(0, DC);
  end;
end;

var
  i, j : integer;
  iMaxRow : integer;
begin
  with DBGrid do
  begin
    // maximal XX Zeilen durchsuchen
    iMaxRow := Min(40, DataSource.DataSet.RecordCount);
    try
      DataSource.DataSet.DisableControls;
      Visible := false; // notwendig, da controls disabled!
      DataSource.DataSet.First;
      // mit Breite der Überschriften inizialisieren
      for i := 0 to Columns.Count - 1 do
      begin
        Columns.Items[i].Width := TextGroesse(Columns[i].Title.Caption, Font).cx + 10;
      end;
      // auf die größte Breite der ersten iMaxRow Zeilen stellen
      for j := 0 to iMaxRow - 1 do
      begin
        for i := 0 to Columns.Count - 1 do
        begin
          Columns.Items[i].Width := Max(TextGroesse(Fields[i].Text, Font).cx + 10, Columns.Items[i].Width);
        end;
        DataSource.DataSet.Next;
      end;
      DataSource.DataSet.First;
    finally
      DataSource.DataSet.EnableControls;
      Visible := true;
    end;
  end;
end;

TiGü 26. Nov 2021 13:09

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1498230)
Aber dann machen wir es noch ein bisserl kürzer ;-)
Delphi-Quellcode:
  for i := 0 to DBGrid1.Columns.Count - 1 do
  begin
    DBGrid1.Columns[i].Width := DBGrid1.Canvas.TextWidth(DBGrid1.Columns[i].Field.AsString);
  end;

Du hast doppelt soviele Aufrufe an TDBGridColumns.GetColumn und 5 Assemblerzeilen mehr (Win32, Debug).
Am Ende "spart" man sich vertikalen Platz, um ihn mit horizontalen Platzbedarf einzutauschen.
Die eingesparten 22 Zeichen wiegen die Nachteile nicht auf.

Delphi.Narium 26. Nov 2021 13:26

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Schön, dann ändern wir auch das:
Delphi-Quellcode:
procedure AutoSizeColumns(AGrid : TDBGrid);
var
  i : Integer;
  LColumn : TColumn;
  LCanvas : TCanvas;
  LField : TField;
begin
  LCanvas := AGrid.Canvas;
  for i := 0 to AGrid.Columns.Count - 1 do
  begin
    LColumn := AGrid.Columns[i];
    LField := LColumn.Field;
    case LField.DataType of
      ftString, ftWideString :
      begin
        LColumn.Width := LCanvas.TextWidth(LField.AsString);
        LColumn.Alignment := taLeftJustify;
      end;
      ftSmallint, ftInteger, ftWord, ftAutoInc, ftLargeint :
      begin
        LColumn.Width := LCanvas.TextWidth(Format('%6.0n',[LField.AsFloat]));
        LColumn.Alignment := taRightJustify;
      end;
      ftFloat, ftCurrency :
      begin
        LColumn.Width := LCanvas.TextWidth(Format('%6.2n',[LField.AsFloat]));
        LColumn.Alignment := taRightJustify;
      end;
      ftDate :
      begin
        LColumn.Width := LCanvas.TextWidth(FormatDateTime('dd.mm:yyyy', LField.AsDateTime]));
        LColumn.Alignment := taLeftJustify;
      end;
      ftTime :
      begin
        LColumn.Width := LCanvas.TextWidth(FormatDateTime('hh:nn:ss', LField.AsDateTime]));
        LColumn.Alignment := taLeftJustify;
      end;
      ftDateTime :
      begin
        LColumn.Width := LCanvas.TextWidth(FormatDateTime('dd.mm.yyyy hh:nn:ss', LField.AsDateTime]));
        LColumn.Alignment := taLeftJustify;
      end;
    else
      LColumn.Width := LCanvas.TextWidth(LField.AsString);
      LColumn.Alignment := taCenter;
    end;
  end;
end;
// Aufruf:
AutoSizeColumns(DBGrid1);
Sieht dann sogar lesbarer aus.
Ob's damit schneller wird, mag und will ich nicht beurteilen.

NicoleWagner 26. Nov 2021 13:52

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
[ case DBGrid_Trades.Columns[i].DataType of
ftString, ftWideString :]

Das will der Compiler nicht:
DBGrid_Trades.Columns[i].DataType

► undeklarierter Bezeichner 'DataType'

Ja, da gab es eine Version hier, die die Spaltenbreite automatisch erzeugen sollte. Vor langem.
Doch sie lief für mich nicht.
Dieses manuelle Setzen ist nämlich extrem mühsam, weil ich mit dem Grid arbeite und die Reihenfolge der Felder immer wieder mal verändere. Daher probierte ich damals die Implementierung sofort. Doch ging nicht.

Delphi.Narium 26. Nov 2021 14:19

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Müsste wohl
Delphi-Quellcode:
DBGrid_Trades.Columns[i].Field.DataType
heißen.

Delphi.Narium 26. Nov 2021 14:28

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Eventuell ließe sich aber im AfterOpen der Query sowas realisieren:
Delphi-Quellcode:
procedure TForm1.AfterOpen(DataSet: TDataSet);
var
  i : Integer;
begin
  for i := 0 to DataSet.Fields.Count - 1 do begin
    case DataSet.Fields[i].DataType of
      ftSmallint, ftInteger, ftWord,
      ftLargeInt, ftAutoInc : TFloatField(DataSet.Fields[i]).DisplayFormat := '###,##0 ';
      ftCurrency, ftFloat  : TFloatField(DataSet.Fields[i]).DisplayFormat := '###,##0.00 ';
      ftDate, ftTime, ftDateTime : TDateTimeField(DataSet.Fields[i]).DisplayFormat := 'dd.mm.yyyy hh:nn:ss';
    end;
  end;
end;
Dann könnte man sich beim Zeichnen des Grids die Formatierungen sparen und bliebe bei der Berechnung der Spaltenbreite bei TiGüs Vorschlag.

NicoleWagner 26. Nov 2021 14:49

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Danke für Deine Mühe. Von der Syntax her wird das jetzt genommen.
Allerdings scheint es nichts zu tun bei mir.

Ich schlug jetzt in der Hilfe nach und - ich bin überfordert. Das ist so komplex, was dort steht, ich verstehe es einfach nicht.

Daher drei Kindermundfragen:

1)
Ist die Option des DBGrids "dgColumnResize" relevant?
Ich schaltete sie ein und schaltete sie aus, doch sehe keinen Unterschied.

2)
Diese Abfragen (aus der anderen Idee)
lb.Caption := DBGrid_Trades.Columns[i].Field.AsString;
DBGrid_Trades.Columns[i].Width := lb.Width;
Was fragt die ab?
Ich brauche ja die längste Ausgabe aus dieser Spalte. Es hat ja die Überschrift eine Breite, der Inhalt manchmal eine andere, und manchmal eine wechselnde.
Eine gute Breite wäre z.B. die erste ausgefüllte Zelle oder aber die breiteste.

3)
Wenn ich habe
DBGrid_Trades.Columns[i]
Wie greife ich da auf die Zellen diese Spalte zu? Ich probierte Fields und items, doch irgendie kommt da nichts Sinnvolles.

Wobei es mir nichts nützt, auf die Query zuzugreifen, denn die Werte in den Spalten werden im Draw-Event ins Grid geschrieben und meiner Einschätzung nach nicht in die Datasource etc.

Und mit den Zugriffen auf die Breite wird die Zählung der Spalten noch mehr fuzzy. Es scheinen da in verschiedenen Zeilen verschiedene Spaltenanzahlen gezählt zu werden.

Delphi.Narium 26. Nov 2021 15:33

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
dgColumnResize heißt: Die Feldern zugeordneten Spalten können in der Größe geändert oder verschoben werden. Anwender können in der Anzeige die Spaltenbreite ändern. Wenn Du mit der Maus in der Titelzeile die Spaltenbreite ändern kannst, sollte das ok sein.
Delphi-Quellcode:
lb.Caption := DBGrid_Trades.Columns[i].Field.AsString;
DBGrid_Trades.Columns[i].Width := lb.Width;
lb ist ein TLabel, bei dem AutoSize auf true steht. Wenn der Caption des Labels ein Text zugewiesen wird, berechnet das Label die erforderliche Breite, um den Text vollständig anzeigen zu können und verändert seine eigene Breite. Die vom Label berechnet Breite wird dann als Spaltenbreite für die Spalte des DBGrid genommen.

Um einen Text in einer Zelle auszugeben, könntest Du im Ereignis OnDrawColumnCell z. B. sowas machen:
Delphi-Quellcode:
DBGrid1.Canvas.TextRect(Rect,2,4,'Dein Text + Feldinhalt: ' + Column.Field.AsString);
Dabei gibt Rect die Koordinaten der Zelle an. 2 ist die X-Koordinate für die Ausgabe, 4 die Y-Koordinate der Ausgabe. Hier kannst Du auch 0 oder andere Werte angeben, um den Text in der Zelle nach Deinen Wünschen zu positionieren. Als vierten Parameter kannst Du eigenen Text ausgeben, oder den Text aus der Zelle oder eine Kombination daraus.

Uwe Raabe 27. Nov 2021 09:29

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Ist das nicht alles sehr kompliziert? Dafür gibt es doch
Delphi-Quellcode:
TColumn.DefaultWidth
? Das verwendet das
Delphi-Quellcode:
DisplayWidth
Property des Fields. Durch geeignete Werte dort (z.B. bei calculated fields) kann man die Spaltenbreite recht einfach steuern.

Wenn es denn tatsächlich von Hand gehen soll, bietet sich auch
Delphi-Quellcode:
TCustomGrid.TextWidthToColWidth
an. Das berücksichtigt wenigstens auch noch die Ränder links und rechts.

NicoleWagner 27. Nov 2021 10:19

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Danke für Deine Antwort.
Ich fürchte, es geht nicht in meinem Fall.

Das liegt daran, dass das DBGrid und die ADQuery irgendwie nicht stimmig miteinander arbeiten, was veränderte Reihenfolgen der DBGrid-Felder einerseits und calculated fields in der ADQuery betrifft. Delphi ist zu doof, die Felder abzuzählen und denkt zudem, sie wären leer (calculated), wenn ich sie im Draw Event fülle. Ja, es ist schlechter Stil, im Draw Event statt im Calc-Event die Werte der Felder zu verändern. Allerdings will ich die Datumswechsel nicht zweimal rechnen, weil ich sie ja in der Grafik ohnehin brauche, um Wochen- und Monatswechsel einzuzeichnen.

Daraus folgt, dass die Breite einiger Felder Null ist, weil das Feld für Delphi leer ist.
Auch wenn ich in Draw etwas umformatiere etwa von 1,2345548886 auf 1,23
so bekommt Delphi das nicht mit und setzt eine sehr große Breite, statt eine schmale.:bouncing4:
Das entschuldigt meine unübliche Vorgangsweise keineswegs. Denn das Draw-Ereignis sollte ja für Formatierungen gut sein, statt dass Delphi davon überfordert ist.

Ich probierte auch, im Draw-Ereignis auf die 2. RecNo zuzugreifen und die Spalten-Breite dort zu setzen. Der wer will die Breite der Überschrift? Ich möchte die Breite des Inhalts.
Warum das meine Anwendung in Zeitlupenmodus versetzte, weiß ich nicht.
Und irgendwann will ich es nicht mehr wissen, weil frustriert von dem Müll, den meine IDE darstellt. Denn all die Fehlzugriffe auf nicht-korrekt zugreifbare Elemente bringen die IDE derart zum Absturz, dass ich sie mehrmals pro Stunde mit dem Taskmanager abschießen muss.

Sondern ich werde die Breiten weiterhin manuell setzen. :cat:

Uwe Raabe 27. Nov 2021 10:44

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Zitat:

Zitat von NicoleWagner (Beitrag 1498297)
Sondern ich werde die Breiten weiterhin manuell setzen.

Das hatte ich auch gar nicht in Frage gestellt. Ich würde dann aber die Spaltenbreite auf DefaultWidth setzen, da diese das TField.DisplayWidth zur Berechnung heranzieht.
Delphi-Quellcode:
  for I := 0 to DBGrid1.Columns.Count - 1 do
    DBGrid1.Columns[I].Width := DBGrid1.Columns[I].DefaultWidth;
Ich halte nicht viel davon, die tatsächlichen Feldwerte der gesamten Tabelle auf ihre aktuelle Länge zu prüfen und davon das Maximum zu nehmen. Das könnte zu unterschiedlichem Aussehen je nach Datenlage führen.

Wenn der Feldinhalt dynamisch ist, dann bestimme einfach einen sinnvollen Wert für DisplayWidth des entsprechenden Feldes. Im Gegensatz zu der Spaltenbreite wird DisplayWidth als Anzahl Zeichen angegeben. Das macht die Sache deutlich einfacher.

NicoleWagner 27. Nov 2021 13:57

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Welche Beitrag jetzt oben und unten steht, ist für mich recht verwirrend.
Wenn ich doppelt antworte, bitte um Entschuldigung:
"Display.Width" funktioniert leider deshalb nicht, weil das lahme Ding diverse Spalten als "leer" ansieht.
Details dazu und welche siehe andere Postings.

Jumpy 29. Nov 2021 14:35

AW: DBGrid1.Columns[0].Width:= .... / geht das auch automatisch?
 
Ist #10 deswegen auch nicht zu gebrauchen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:59 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz