Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Spaltenbreite DBGrid automatisch anpassen (https://www.delphipraxis.net/158331-spaltenbreite-dbgrid-automatisch-anpassen.html)

Jumpy 14. Feb 2011 07:40

Spaltenbreite DBGrid automatisch anpassen
 
Hallo,

ich suche für das DBGrid (oder vllt. generell für Grids) die Möglichkeit die Spaltenbreite automatisch anzupassen, so wie die bei Excel das Autofit.

Hintergrund ist, dass viele Spalten des Grid einfach viel zu breit sind. Stellt es z.B. ein DB-Feld dar, das ein CHAR(250) Feld ist, so ist die entsprechende Spalte einfach sehr breit, um halt die theor. 250 Zeichen anzuzeigen. Der größte tatsächliche Feldinhalt ist aber z.B. nur 50 Zeichen. Dann könnte die Breite viel kleiner sein.

Hinzukommt, dass wenn die Spaltenbreite sehr groß ist (größer als z.B. die Bildschirmbreite) ist es sehr mühselig, wenn nicht unmöglich, das per Hand klein zu ziehen. Mein Workarround in solchen Fällen ist es, alle SPaltenbreiten erstmal auf 50 zu setzen und dann so groß zu ziehen, wie ich sie brauche. Das ist aber auch nicht elegant.

Drum die Frage, gibt es bei Grids so eine Eigenschaft? Falls nicht wie baut man das ein, bzw. hat jemand schonmal was diesbezüglich gemacht?

nahpets 14. Feb 2011 08:00

AW: Spaltenbreite DBGrid automatisch anpassen
 
Hallo,

eine "dumme" Idee für die Schnelle:

Ermittle die maximale Länge des Spalteninhaltes.

Nimm den längsten Text und weise diesen der Caption eines Labels zu. Das Label muss AutoSize = True haben. Es bekommt damit die entsprechende Breite des zugewiesenen Textes.
Nun nimmst Du den Wert der Eigenschaft Width des Labels rechnest noch 8 (oder so) für ein bisserl Rand dazu und weist diesen Wert als Breite der (DB)Gridspalte zu.

Und damit ist das "AutoFit" vorhanden.

Das Label kannst Du entweder unsichtbar irgendwo auf das Formular legen oder zur Laufzeit erstellen.
Eine entsprechende Routine dem TitleClick der Grids zuweisen und Du hast fast den Effekt von Excel. Ob man bei einem Grid auch eine Ereignisroutine für einen Doppelklick auf die Trennlinie zwischen zwei Spaltenköpfen legen kann, weiß ich nicht.

DeddyH 14. Feb 2011 08:04

AW: Spaltenbreite DBGrid automatisch anpassen
 
Dazu muss aber auch die gesamte Datenmenge durchlaufen werden, was ggf. schon ein wenig dauern kann.

FaTaLGuiLLoTiNe 14. Feb 2011 08:11

AW: Spaltenbreite DBGrid automatisch anpassen
 
Und wenn man trotzdem den Weg von naphets gehen will, dann könnte man sich das Label sparen und stattdessen mit TCanvas.TextWidth arbeiten.

DeddyH 14. Feb 2011 08:14

AW: Spaltenbreite DBGrid automatisch anpassen
 
Jepp, aber dabei nicht vergessen, dass man auch den richtigen Font zuweisen muss, sonst rechnet man mit falschen Werten.

Jumpy 14. Feb 2011 08:34

AW: Spaltenbreite DBGrid automatisch anpassen
 
Zitat:

Zitat von DeddyH (Beitrag 1081488)
Dazu muss aber auch die gesamte Datenmenge durchlaufen werden, was ggf. schon ein wenig dauern kann.

Ich überlege gerade. Im Falle von DBGrid liegt ja eine Datenmenge aus einer DB zugrunde. Anstatt die evtl. große Datenmenge zu durchlaufen, wäre es nicht schneller eine Funktion zum zuweisen der Breite zu schreiben, die die maximalen Feldbreiten aus der DB holt, d.h. die DB die Arbeit machen lassen?
Z.B. TADOQuery q:
Delphi-Quellcode:
q.SQL.Text:= 'SELECT Max(Length(Feld1)) as F1, Max(Length(Feld2))as F2,... from MyTable';
q.Open;
for i:=0 to q.RecordCount-1 do
  Grid.Columns[i].Width:=q.Fields[i].AsSingle * KonstanterFaktor;

DeddyH 14. Feb 2011 08:37

AW: Spaltenbreite DBGrid automatisch anpassen
 
So wie gezeigt wird es wohl nicht gehen, Du brauchst ja keine Maximalwerte, sondern die Metadaten. Wenn Du die ausgelesen hast könnte das schon funktionieren, zumindest für Datentypen, deren "Ausgabelänge" sich konkret ermitteln lässt.

nahpets 14. Feb 2011 08:38

AW: Spaltenbreite DBGrid automatisch anpassen
 
Hallo,
Zitat:

Zitat von DeddyH (Beitrag 1081488)
Dazu muss aber auch die gesamte Datenmenge durchlaufen werden, was ggf. schon ein wenig dauern kann.

wieso?

Code:
Select max(length(Spalte1)) as a, max(length(Spalte2)) as b, ... from Tabelle
geht doch bestimmt auch und muss einmal beim Programmstart aufgerufen werden. Änderungen zur Programmlaufzeit wollen wir hier mal geflissentlich ignorieren.
Natürlich müssen die Fonts übereinstimmen, da ich immer die gleiche Schrift nutze, tritt das Problem nicht auf.

Edit: @Jumpy: Die Idee hatte ich auch, nur leider zu spät ;-)

DeddyH 14. Feb 2011 08:43

AW: Spaltenbreite DBGrid automatisch anpassen
 
Das klappt aber auch nur bei fixed Fonts, die Länge des enthalteten Strings kann also auch kein eindeutiges Kriterium sein. In Arial sind 10 'X' breiter als 20 'i'. Also entweder doch jeden einzelnen Datensatz anfassen oder eben wie gesagt Metadaten auslesen und Breite auf die größtmögliche stellen. Oder halt einen fixed Font verwenden.

Jumpy 14. Feb 2011 09:00

AW: Spaltenbreite DBGrid automatisch anpassen
 
Zitat:

Zitat von Jumpy (Beitrag 1081495)
Delphi-Quellcode:
  Grid.Columns[i].Width:=q.Fields[i].AsSingle * KonstanterFaktor;

Meine Variable KonstanterFaktor sollte die Breite eines Buchstabens repräsentieren, wobei das so gedacht ist, dass es entweder die Durchschittsbreite (die man irgendwie erechnen müsste) oder eine Maximalbreite (Breite von Buchstabe "W"?) um auf der sicheren Seite zu sein. Und natürlich abhängig von der Schriftart/-größe änderbar.

Das mit "Max(Length(Feld1)) as F1" macht natürlich nur bei String-Feldern Sinn, aber die sind es auch, die den meisten Ärger verursachen. Datum und Zahl-Felder sind meist nicht so übertrieben breit.


Ich werd mal versuchen ein Beispiel-Unit zu bauen und hochzuladen.

DeddyH 14. Feb 2011 09:10

AW: Spaltenbreite DBGrid automatisch anpassen
 
Wenn Du mit MAX die Maximalbreite ermitteln kannst, kannst Du auch mit AVG die Durchschnittsbreite ermitteln ;). Multiplizierst Du das dann mit einem "breiten" Buchstaben wie dem angegebenen 'W' ist das zwar nicht der Weisheit letzter Schluss, aber IMO ein akzeptabler Kompromiss, wenn es nicht auf einzelne Pixel ankommt.

nahpets 14. Feb 2011 09:35

AW: Spaltenbreite DBGrid automatisch anpassen
 
Praktische Breite bei variabler Schrift ist
Code:
maximale Buchstabenanzahl
in W
* 75%
Damit bin ich in der Regel gut zurechtgekommen.

Bei Datum und Zahlen kann man ebenfalls die maximale Länge bestimmen und als Zeichen die 0 statt das W nehmen.

Sir Rufo 14. Feb 2011 10:35

AW: Spaltenbreite DBGrid automatisch anpassen
 
Hab ich mal von irgendwo aus dem Netz kopiert und noch etwas überarbeitet
Delphi-Quellcode:
unit uGridColumnWidth;

interface

uses
  DBGrids;

procedure SetGridColumnWidths( AGrid : TDBGrid );

implementation

uses
  DB, SysUtils;

procedure SetGridColumnWidths( AGrid : TDBGrid );
  const
    DEFBORDER = 10;
  var
    temp, idx, bcidx : integer;
    lmax : array of integer;
    bm : TBookmark;
  begin
    with AGrid do
      begin
        SetLength( lmax, Columns.Count );

        if dgTitles in AGrid.Options then
          begin

            // Measure Title

            Canvas.Font := AGrid.TitleFont;
            for idx := 0 to Columns.Count - 1 do
              if Columns[ idx ].Visible then
                lmax[ idx ] := Canvas.TextWidth( Columns[ idx ].Title.Caption )
                  + DEFBORDER;

          end;

        // Measure Data

        Canvas.Font := AGrid.Font;

        // Anzeige abschalten
        DataSource.DataSet.DisableControls;
        try
          // Aktuellen Datensatz merken
          bm := DataSource.DataSet.GetBookmark;
          // Zum ersten Datensatz springen
          DataSource.DataSet.First;
          // Alle Datensätze durchwandern
          while not DataSource.DataSet.EOF and ( DataSource.DataSet.RecNo < DataSource.DataSet.RecordCount ) do
            begin
              // Alle Spalten durchwandern
              for idx := 0 to Columns.Count - 1 do

                // Ist die Spalte sichtbar?
                if Columns[ idx ].Visible then
                  begin

                    // Breite des Inhalts ermitteln
                    temp := Canvas.TextWidth
                      ( trim( Columns[ idx ].Field.DisplayText ) ) + DEFBORDER;

                    // nur die maximale Breite merken
                    if temp > lmax[ idx ] then
                      lmax[ idx ] := temp;

                  end;

              // Nächster Datensatz
              DataSource.DataSet.Next;
            end;

          // Zum Datensatz zurückspringen, der eingangs gewählt war

          if DataSource.DataSet.BookmarkValid( bm ) then
            DataSource.DataSet.GotoBookmark( bm )
          else
            DataSource.DataSet.First;

        finally
          // Anzeige einschalten
          DataSource.DataSet.EnableControls;
        end;

        // Spaltenbreiten ändern

        for idx := 0 to Columns.Count - 1 do
          if Columns[ idx ].Visible then
            if lmax[ idx ] > 0 then
              Columns[ idx ].Width := lmax[ idx ];
      end;
  end; { SetGridColumnWidths }
end.

Jumpy 14. Feb 2011 11:24

AW: Spaltenbreite DBGrid automatisch anpassen
 
Danke. Das ist zwar die "Ich acker mal die ganze Datenmenge ab"-Variante, aber dafür cool gelöst (soweit ich das als Newbie beurteilen kann). Wenn ich mal Zeit hab (als Azubi hab ich die vllt.) mach ich mir den Spass und teste mal ab welcher Datenmenge es performanter wird, die Arbeit von der Datenbank erledigen zu lassen. Wobei die angezeigte Datenmenge im Grid in der Regel so klein ist, dass obiges bestimmt schneller ist.
Wer sich dagegen 12 Millionen Datensätz im Grid anzeigen läßt ist selber Schuld.:-D

Bummi 14. Feb 2011 11:31

AW: Spaltenbreite DBGrid automatisch anpassen
 
als gäbe noch die Q&D Option zu Laufzeit im OnPaint über Canvas.TextWidth die Spalten zu vergrößern, sofern sie zu schmal sind.
Delphi-Quellcode:
   if DBGrid1.Canvas.TextWidth(Column.Field.AsString) > (Rect.Right - Rect.Left) then
    begin
    Column.Width := DBGrid1.Canvas.TextWidth(Column.Field.AsString);
    end;

DeddyH 15. Feb 2011 07:46

AW: Spaltenbreite DBGrid automatisch anpassen
 
Das könnte aber unschöne Effekte beim Scrollen zur Folge haben.

Bummi 15. Feb 2011 08:58

AW: Spaltenbreite DBGrid automatisch anpassen
 
Zitat:

Das könnte aber unschöne Effekte beim Scrollen zur Folge haben.
Die Spalten werden breiter wenn nötig, eine gewisse Dynamik ist gegeben, das ist richtig.

David Martens 15. Feb 2011 14:01

AW: Spaltenbreite DBGrid automatisch anpassen
 
Also wir haben das so gelöst:
Delphi-Quellcode:
function TextGroesse(const Text : string; Font : TFont = nil) : TSize;
var
  DC : hDC;
  F : hFont;
  R : TRect;
begin
  F := 0;
  DC := GetDC(0);
  try
    if Font <> nil then
      F := SelectObject(DC, Font.Handle);
    if not GetTextExtentPoint32(DC, 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;

procedure SetColumnWidth(DBGrid : TDBGrid);
var
  i, j : integer;
  iMaxRow : integer;
begin
  with DBGrid do
  begin
    //     maximal || 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, Canvas.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, Canvas.Font).cx + 10, Columns.Items[i].Width);
        end;

        DataSource.DataSet.Next;
      end;
      DataSource.DataSet.First;
    finally
      DataSource.DataSet.EnableControls;
      Visible := true;
    end;
  end;
end;
Leider mussten wir GetTextExtentPoint32 für die Textbreite verwenden, da Canvas.TextWidth manchmal einen fehlerhaften (zu kleinen) Wert liefert.

Bemerkungen:
1. Da wir z.T. sehr viele Datensätze haben können > 10 Mio. haben wir die Breitenberechnung auf 40 DS beschränkt.
2. An die Textbreite wird + 10 angehängt um einen kleinen Abstand zur Begrenzung zu haben.
3. Wir haben auf Bookmarks verzichtet, da die Berechnung der Breite nur beim inizialen Laden der Daten notwenig ist.
4. Besonders wichtig sind DisableControls und Visible := false, damit wird die Funktion um ein vielfaches schneller.

Gruß David

mkinzler 15. Feb 2011 16:26

AW: Spaltenbreite DBGrid automatisch anpassen
 
Man könnte auch den Canvas fragen, wie viele Pixel der Text gerendert hat.

nahpets 16. Feb 2011 07:08

AW: Spaltenbreite DBGrid automatisch anpassen
 
Hallo,

oder mal das TJVDBGrid von den Jedis anschauen, es hat ein Attribut für maximale Spaltebreite, für minimale Spaltenbereite, kann, wenn ich das richtig sehe, auch die Spaltenbreite automatisch auf das richtige Maß bringen.

Könnte von daher eine Alternative zum "Selbermachen" sein.

andydp 10. Okt 2012 14:57

AW: Spaltenbreite DBGrid automatisch anpassen
 
Hallo Leute

Hab das StringGrid von

http://www.tmssoftware.com/site/advgrid.asp


genommen. Ist genial, damit kannn man alles machen.
Mit einer einfachen Programmzeile:

Code:
AdvStringGrid1.AutoSizeColumns(True, 4);
Werden die Spalten automatisch an die Textgrösse angepasst.
Ist nicht ganz billig, aber wenn man die Zeit fürs "Rumwursteln" rechnet. Bestimmt nicht die schlechteste Wahl.

Furtbichler 10. Okt 2012 19:02

AW: Spaltenbreite DBGrid automatisch anpassen
 
Das Argument, das das Ganze bei 12 Mio Datensätzen vielleicht doch etwas länger dauert kann man dadurch begegnen, das man einfach vorgibt, das die Messung der Spaltenbreite nach X Zeilen beendet wird. So macht das DevExpress. Dann werden nur die ersten (z.B.) 500 Zeilen eingelesen per Canvas.GetTextWidth ausgemessen. Das reicht eigentlich immer, denn wer scrollt schon 500 Zeilen nach unten.

Das dynamische Anpassen hatte ich mal gemacht, als ich paging implementiert habe. Man freut sich, das es so schön funktioniert, aber irgendwie nervt es nach ner WEile.

Perlsau 11. Okt 2012 08:31

AW: Spaltenbreite DBGrid automatisch anpassen
 
Ich lasse gewöhnlich den Anwender entscheiden, wie breit er seine Spalten haben möchte, und speichere die Abmessungen in einer extra Tabelle, in der nur solche Anwender-Daten hinterlegt werden. Meine DBGrids starten meist – d.h. wenn es so viele Felder in der Tabelle gibt, daß sie auch auf einem sehr breiten Screen nicht alle gleichzeitig darstellbar sind – mit einer Standard-Spaltenbreite, die sich nicht an der möglichen Länge der Tabellen-Inhalte orientiert, sondern an der Länge des Spalten-Titels. Der Anwender kann sich dann seine bevorzugten Spaltenbreiten selber einstellen, seine Einstellungen werden beim Beenden des Programms (oder auch, wenn man will, nach jeder Spaltenbreiten-Änderung) in der DB gespeichert und bei Programmstart wieder eingelesen und angewendet:

Delphi-Quellcode:
// Spaltenbreiten DBGrid_V_Artikel in FormMain
     z := FormMain.DBGrid_V_Artikel.Columns.Count -1;
     FOR i := 0 TO z DO
     BEGIN
          Aus := IntToStr(i);
          IF i < 10 THEN Aus := N + Aus;
          Aus := 'TAB_ARTIKEL_' + Aus;
          FormMain.DBGrid_V_Artikel.Columns[i].Width := DatMod.Dset_Tabellen.FieldByName(Aus).AsInteger;
     END;

joachimd 12. Okt 2012 08:22

AW: Spaltenbreite DBGrid automatisch anpassen
 
Zitat:

Zitat von Perlsau (Beitrag 1186616)
Ich lasse gewöhnlich den Anwender entscheiden, wie breit er seine Spalten haben möchte, und speichere die Abmessungen in einer extra Tabelle, in der nur solche Anwender-Daten hinterlegt werden.

Dito, nur streame ich die DBGrid.Columns in ein Blob-Feld. Damit spart man sich das Iterieren und hat aber auch gleich andere Eigenschaften (wie zB Lookup-Listen u.ä.) mit drin.

Perlsau 12. Okt 2012 08:26

AW: Spaltenbreite DBGrid automatisch anpassen
 
Zitat:

Zitat von joachimd (Beitrag 1186719)
Zitat:

Zitat von Perlsau (Beitrag 1186616)
Ich lasse gewöhnlich den Anwender entscheiden, wie breit er seine Spalten haben möchte, und speichere die Abmessungen in einer extra Tabelle, in der nur solche Anwender-Daten hinterlegt werden.

Dito, nur streame ich die DBGrid.Columns in ein Blob-Feld. Damit spart man sich das Iterieren und hat aber auch gleich andere Eigenschaften (wie zB Lookup-Listen u.ä.) mit drin.

Die Einstellungen in ein Blobfeld zu streamen ist mir noch nicht in den Sinn gekommen, scheint aber eine gute Idee zu sein. Muß ich bei Gelegenheit mal ausprobieren. Vielen Dank für deine Anregung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:27 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