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/)
-   -   Delphi VirtualTreeview als Grid optimale Spaltenbreite berechnen (https://www.delphipraxis.net/212626-virtualtreeview-als-grid-optimale-spaltenbreite-berechnen.html)

Codehunter 7. Mär 2023 09:42

VirtualTreeview als Grid optimale Spaltenbreite berechnen
 
Hallo!

Nach langer Zeit hab ich mal wieder ein Problem an dem ich mir die Zähne ausbeiße:

Ich verwende einen VirtualTreeview als Grid. Dort wo ich das Grid initialisiere, stehen mir schon folgende Werte zur Verfügung:
  1. Spaltentitel (Headertext) jeder Spalte
  2. längster Grid-Celltext jeder Spalte

Ich versuche nun, bei der Initialisierung für jede Spalte die optimale Breite zu berechnen. Das Ziel soll sein, dass jede Spalte genau so breit ist, dass weder der Headertext noch die Zellenwerte mit "..." abgekürzt werden. Die Spalten sind zudem sortierbar, d.h. es kommt ggf. im Header noch das SortGlyph dazu.

Ich kann nun zwar mit Canvas.TextWidth die Breite des Headertextes sowie des längsten Zellentextes berechnen. Aber ich habe festgestellt, dass mit statischen "Zugaben" für Headerbreite und Zellenbreite kein Blumentopf zu gewinnen ist. Ich kriegs zwar hin dass alle Spalten ohne "..." angezeigt werden, aber manche sind viel zu breit.

Wenn ich mir z.B. in VirtualTrees.pas die TVirtualTreeColumn.ComputeHeaderLayout anschaue, dann ahne ich auch warum: Da stecken extrem komplizierte Berechnungen dahinter.

Nun frage ich mich: Hat der Virtualtree vielleicht schon eine eingebaute Funktion, die optimale Spaltenbreite zu berechnen, der ich den Headertext und den längsten Zellentext mitgeben kann?

Grüße
Cody

KodeZwerg 7. Mär 2023 12:14

AW: VirtualTreeview als Grid optimale Spaltenbreite berechnen
 
Ich habe den VST in Delphi leider nie installiert aber in Lazarus.
Dort gibt es "VST.Header.AutoFitColumns()" als Methode.
Delphi-Quellcode:
procedure TVTHeader.AutoFitColumns(Animated: Boolean = True; SmartAutoFitType: TSmartAutoFitType = smaUseColumnOption;
  RangeStartCol: Integer = NoColumn; RangeEndCol: Integer = NoColumn);

  //--------------- local functions -------------------------------------------

  function GetUseSmartColumnWidth(ColumnIndex: TColumnIndex): Boolean;

  begin
    Result := False;
    case SmartAutoFitType of
      smaAllColumns:
        Result := True;
      smaNoColumn:
        Result := False;
      smaUseColumnOption:
        Result := coSmartResize in FColumns.Items[ColumnIndex].FOptions;
    end;
  end;

  //----------------------------------------------------------------------------

  procedure DoAutoFitColumn(Column: TColumnIndex);

  begin
    with FColumns do
      if ([coResizable, coVisible] * Items[FPositionToIndex[Column]].FOptions = [coResizable, coVisible]) and
            DoBeforeAutoFitColumn(FPositionToIndex[Column], SmartAutoFitType) and not TreeView.OperationCanceled then
      begin
        if Animated then
          AnimatedResize(FPositionToIndex[Column], Treeview.GetMaxColumnWidth(FPositionToIndex[Column],
            GetUseSmartColumnWidth(FPositionToIndex[Column])))
        else
          FColumns[FPositionToIndex[Column]].Width := Treeview.GetMaxColumnWidth(FPositionToIndex[Column],
            GetUseSmartColumnWidth(FPositionToIndex[Column]));

        DoAfterAutoFitColumn(FPositionToIndex[Column]);
      end;
  end;

  //--------------- end local functions ----------------------------------------

var
  I: Integer;
  StartCol,
  EndCol: Integer;

begin
  StartCol := Max(NoColumn + 1, RangeStartCol);

  if RangeEndCol <= NoColumn then
    EndCol := FColumns.Count - 1
  else
    EndCol := Min(RangeEndCol, FColumns.Count - 1);

  if StartCol > EndCol then
    Exit; // nothing to do

  TreeView.StartOperation(okAutoFitColumns);
  try
    if Assigned(TreeView.FOnBeforeAutoFitColumns) then
      TreeView.FOnBeforeAutoFitColumns(Self, SmartAutoFitType);

    for I := StartCol to EndCol do
      DoAutoFitColumn(I);

    if Assigned(TreeView.FOnAfterAutoFitColumns) then
      TreeView.FOnAfterAutoFitColumns(Self);

  finally
    Treeview.EndOperation(okAutoFitColumns);
  end;
end;
Meintest Du sowas?

Codehunter 7. Mär 2023 17:28

AW: VirtualTreeview als Grid optimale Spaltenbreite berechnen
 
Jain. AutoFit habe ich auch probiert. Leider ist diese Methode extremst langsam bei meinen Datenmengen. Deshalb ermittle ich ja schon vorab, was der längste Zelleninhalt je Spalte ist. Da ich eine Monospace-Schriftart verwende ist die Anzahl Buchstaben gleichbedeutend mit der größten Pixelanzahl. Ich kann also schon beim Initialisieren des VST (Rootnodecount setzen und entsprechende Anzahl Column-Objekte erzeugen) festlegen, wie breit jede Spalte sein soll. Das würde ich gar nicht nachgelagert über Eventhandler machen wollen.

Der Knackpunkt ist der ColumnHeader mit dem Sort-Glyph. Wenn das beides zusammen mehr Platz braucht als der längste Zelleninhalt im Grid, klappt die Kalkulation nicht mehr. Warum auch immer.

taveuni 8. Mär 2023 10:26

AW: VirtualTreeview als Grid optimale Spaltenbreite berechnen
 
Zitat:

Zitat von Codehunter (Beitrag 1519630)
Jain. AutoFit habe ich auch probiert. Leider ist diese Methode extremst langsam bei meinen Datenmengen. Deshalb ermittle ich ja schon vorab, was der längste Zelleninhalt je Spalte ist. Da ich eine Monospace-Schriftart verwende ist die Anzahl Buchstaben gleichbedeutend mit der größten Pixelanzahl. Ich kann also schon beim Initialisieren des VST (Rootnodecount setzen und entsprechende Anzahl Column-Objekte erzeugen) festlegen, wie breit jede Spalte sein soll. Das würde ich gar nicht nachgelagert über Eventhandler machen wollen.

Der Knackpunkt ist der ColumnHeader mit dem Sort-Glyph. Wenn das beides zusammen mehr Platz braucht als der längste Zelleninhalt im Grid, klappt die Kalkulation nicht mehr. Warum auch immer.

Du kannst beim Initialsieren die (Mindest-) Headerspaltenbreite berechnen mit Zugabe für den Glyph. Dann die Mindestbreite setzen.

Delphi-Quellcode:
procedure TFrameAlarmsVst.InitVst;
var
  col: TVirtualTreeColumn;
  b: TBitmap;
begin
  b:= TBitMap.Create;
  try
    b.Canvas.Font.Assign(vstAlarms.Header.Font);

    col:= self.vstAlarms.Header.Columns.Add;
    col.Text:= _('Alarmart');
    col.MinWidth:= b.Canvas.TextWidth(col.Text) + VST_COLUMN_GLYPH_WIDTH_ADDITION;

  finally
    b.Free;
  end;
Wenn dann Deine Value Spaltenbreite kleiner ist als die col.MinWidth musst Du gar nichts berechnen/setzen.

freejay 8. Mär 2023 11:01

AW: VirtualTreeview als Grid optimale Spaltenbreite berechnen
 
Also ich mache das bei StringGrids mit sehr vielen Zeilen so, dass ich die Breite der Spalten beim Zeichnen berechne: Ist die Spalte zu schmal für den neu gezeichneten Wert,
dann vergrößere ich sie in diesem Moment. Das bewirkt zwar, dass die Spalten beim Scrollen oft breiter werden. Der Vorteil ist aber, dass das automatische Berechnen der Spaltenbreiten praktisch sofort passiert und keine messbare Zeit nach dem Füllen der Tabelle (oder bei Dir: eines Trees) dafür verbraten wird. Ist aber sicher nicht jedermanns Sache.


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