Mirror

Paint a complete TTreeView on a canvas (Views: 100)


Problem/Question/Abstract:

How to paint a complete TTreeView on a canvas

Answer:

I recently implemented a procedure to paint a TTreeView component to a canvas, including the images, state images and so on, and not only the visible nodes, but also those that do not fit in the client area.

unit TreePaint;

interface

uses
  Windows, Graphics, ComCtrls;

procedure TreeViewPaintTo(ATreeView: TTreeView; FullExpand: Boolean;
  ACanvas: TCanvas; X, Y: Integer);

implementation

procedure TreeViewPaintTo(ATreeView: TTreeView; FullExpand: Boolean;
  ACanvas: TCanvas; X, Y: Integer);

var
  OffsetX, OffsetY: Integer;

  procedure DrawButton(X, Y: Integer; Expanded: Boolean);
  var
    R: TRect;
  begin
    ACanvas.Pen.Color := clGray;
    ACanvas.Pen.Style := psSolid;
    ACanvas.Rectangle(X - 5, Y - 5, X + 4, Y + 4);
    ACanvas.Pixels[X + 1, Y - 1] := clBlack;
    ACanvas.Pixels[X, Y - 1] := clBlack;
    ACanvas.Pixels[X - 1, Y - 1] := clBlack;
    ACanvas.Pixels[X - 2, Y - 1] := clBlack;
    ACanvas.Pixels[X - 3, Y - 1] := clBlack;
    if (not Expanded) then
    begin
      ACanvas.Pixels[X - 1, Y + 1] := clBlack;
      ACanvas.Pixels[X - 1, Y] := clBlack;
      ACanvas.Pixels[X - 1, Y - 1] := clBlack;
      ACanvas.Pixels[X - 1, Y - 2] := clBlack;
      ACanvas.Pixels[X - 1, Y - 3] := clBlack;
    end;
  end;

  procedure DrawHorizLine(X, Y: Integer; HasButton: Boolean);
  begin
    if (HasButton) then
      X := X + 5;
    ACanvas.Pixels[X, Y] := clGray;
    ACanvas.Pixels[X + 2, Y] := clGray;
    ACanvas.Pixels[X + 4, Y] := clGray;
  end;

  procedure DrawVertLine(X, Y0, Y1: Integer; HasButton: Boolean);
  begin
    if (HasButton) then
      Y0 := Y0 + 5;
    while (Y0 <= Y1) do
    begin
      ACanvas.Pixels[X, Y0] := clGray;
      inc(Y0, 2);
    end;
  end;

  procedure TreeNodePaintTo(ATreeNode: TTreeNode; ACanvas: TCanvas);
  var
    FirstNode: Boolean;
    CurNode: TTreeNode;
    NewX, NewY, CurX, CurY, StateY, ImageY: Integer;
  begin
    CurNode := ATreeNode;
    FirstNode := True;
    while (CurNode <> nil) do
    begin
      if (not (CurNode.IsVisible or FullExpand)) then
        Exit;
      {Compute Start X and Y}
      NewX := X + (CurNode.Level * OffsetX);
      NewY := Y + (OffsetY div 2);
      {Line to sibling node}
      if (ATreeView.ShowLines) then
      begin
        if (not FirstNode) then
        begin
          if (ATreeView.ShowRoot or (CurNode.Level > 0)) then
            DrawVertLine(NewX - 1, CurY - (OffsetY div 2) + 1, NewY, True);
        end
        else
        begin
          FirstNode := False;
          {Line to parent node}
          if (CurNode.Parent <> nil) then
            DrawVertLine(NewX - 1, Y - (OffsetY div 2) + 1, NewY, True)
        end;
      end;
      {Update Sibling offsets}
      CurX := NewX;
      CurY := NewY;
      if (ATreeView.ShowRoot or (CurNode.Level > 0)) then
      begin
        if (ATreeView.ShowButtons) then
        begin
          {Draw the button}
          if (CurNode.HasChildren) then
          begin
            DrawButton(NewX, NewY, FullExpand or CurNode.Expanded);
            CurY := CurY + 9;
          end;
          if (ATreeView.ShowLines) then
            DrawHorizLine(NewX, NewY, CurNode.HasChildren);
        end
        else if (ATreeView.ShowLines) then
          DrawHorizLine(NewX, NewY, False);
      end;
      {Update X Offset}
      NewX := NewX + 9;
      {State Image}
      if (Assigned(ATreeView.StateImages)) then
      begin
        {Draw the State Image}
        StateY := Y + ((OffsetY - ATreeView.StateImages.Height) div 2);
        ATreeView.StateImages.Draw(ACanvas, NewX, StateY, CurNode.StateIndex);
        {Update X Offset}
        NewX := NewX + ATreeView.StateImages.Width;
      end;
      {Image}
      if (Assigned(ATreeView.Images)) then
      begin
        {Draw the Image}
        ImageY := Y + ((OffsetY - ATreeView.Images.Height) div 2);
        ATreeView.Images.Draw(ACanvas, NewX, ImageY, CurNode.ImageIndex);
        {Update X Offset}
        NewX := NewX + ATreeView.Images.Width;
      end;
      ACanvas.TextOut(NewX, Y, CurNode.Text);
      {Update Y Offset}
      Y := Y + OffsetY;
      {Paint Child Nodes}
      if (CurNode.GetFirstChild <> nil) then
        TreeNodePaintTo(CurNode.GetFirstChild, ACanvas);
      {Paint sibling nodes}
      CurNode := CurNode.GetNextSibling;
    end;
  end;
begin
  {Compute Offsets}
  OffsetX := 19;
  OffsetY := 5 * ACanvas.TextHeight('|') div 4;
  if (Assigned(ATreeView.StateImages)) and (ATreeView.StateImages.Height > OffsetY)
    then
    OffsetY := ATreeView.StateImages.Height;
  if (Assigned(ATreeView.Images)) and (ATreeView.Images.Height > OffsetY) then
    OffsetY := ATreeView.Images.Height;
  if (ATreeView.ShowRoot) then
    X := X + 10;
  TreeNodePaintTo(ATreeView.Items.GetFirstNode, ACanvas);
end;

end.

<< Back to main page