Search code examples
delphiautosizevirtualtreeviewtvirtualstringtree

How to auto size spanned column in TVirtualStringTree?


I have simple example of VirtualStringTree (VST) with 4 columns and in 2nd or 3rd column I could have more text that fit the default column width. I have hoAutoSpanColumns enabled so the large text will overlap other columns, if they are empty. This works good. The problem is when text in 2nd or 3rd column should span over the full length of VST. In this case the large columns only extend to the end of last (4th) column.

Here is how 2nd column in line 3 only spans over 3rd and 4th columns and then it stops at the width of 4th column:

enter image description here

If I use AutoFitColumns to auto size 2nd column:

VST1.Header.AutoFitColumns(false, smaUseColumnOption,1,1);

then it pushes 3rd and 4th columns so they start at the end of 2nd column:

enter image description here

But I need 3rd and 4th column to stay at the same positions, like this:

enter image description here

Is there a way to auto fit 2nd column to text, but keep 3rd and 4th columns at the same positions as they were originally?


Solution

  • There is no built-in way to autofit header to a spanned column text at this time. To autosize a given column to see the whole spanned text you can write a code like this (for single node). Do note that autospan feature in VT doesn't support RTL reading at this time (so this code doesn't as well):

    type
      TVirtualStringTree = class(VirtualTrees.TVirtualStringTree)
      protected
        function GetSpanColumn(Node: PVirtualNode): TColumnIndex; virtual;
      public
        procedure AutoFitSpanColumn(DestColumn: TColumnIndex; Node: PVirtualNode);
      end;
    
    implementation
    
    { TVirtualStringTree }
    
    function TVirtualStringTree.GetSpanColumn(Node: PVirtualNode): TColumnIndex;
    begin
      { this returns visible span column for the given node, InvalidColumn otherwise }
      Result := Header.Columns.GetLastVisibleColumn;
      while ColumnIsEmpty(Node, Result) and (Result <> InvalidColumn) do
        Result := Header.Columns.GetPreviousVisibleColumn(Result);
    end;
    
    procedure TVirtualStringTree.AutoFitSpanColumn(DestColumn: TColumnIndex; Node: PVirtualNode);
    var
      ColsWidth: Integer;
      SpanWidth: Integer;
      SpanOffset: Integer;
      SpanColumn: TColumnIndex;
    begin
      SpanColumn := GetSpanColumn(Node);
    
      if SpanColumn <> InvalidColumn then
      begin
        { get the total width of the header }
        ColsWidth := Header.Columns.TotalWidth;
        { get the width of the span text cell as it would be autosized }
        SpanWidth := DoGetNodeWidth(Node, SpanColumn) + DoGetNodeExtraWidth(Node, SpanColumn) +
          DoGetCellContentMargin(Node, SpanColumn).X + Margin;
        { and get the left position of the span cell column in header }
        SpanOffset := Header.Columns[SpanColumn].GetRect.Left;
        { now, the width of the autosized column we increase by the subtraction of the fully
          visible span cell width and all columns width increased by offset of the span cell
          column; in other words, we'll just increase or decrease width of the DestColumn to
          the difference of width needed for the span column to be fully visible, or "fit" }
        Header.Columns[DestColumn].Width := Header.Columns[DestColumn].Width +
          SpanWidth - ColsWidth + SpanOffset;
      end;
    end;