| 
			Author: Tomas Rutkauskas
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.
1   unit TreePaint;
2   
3   interface
4   
5   uses
6     Windows, Graphics, ComCtrls;
7   
8   procedure TreeViewPaintTo(ATreeView: TTreeView; FullExpand: Boolean;
9     ACanvas: TCanvas; X, Y: Integer);
10  
11  implementation
12  
13  procedure TreeViewPaintTo(ATreeView: TTreeView; FullExpand: Boolean;
14    ACanvas: TCanvas; X, Y: Integer);
15  
16  var
17    OffsetX, OffsetY: Integer;
18  
19    procedure DrawButton(X, Y: Integer; Expanded: Boolean);
20    var
21      R: TRect;
22    begin
23      ACanvas.Pen.Color := clGray;
24      ACanvas.Pen.Style := psSolid;
25      ACanvas.Rectangle(X - 5, Y - 5, X + 4, Y + 4);
26      ACanvas.Pixels[X + 1, Y - 1] := clBlack;
27      ACanvas.Pixels[X, Y - 1] := clBlack;
28      ACanvas.Pixels[X - 1, Y - 1] := clBlack;
29      ACanvas.Pixels[X - 2, Y - 1] := clBlack;
30      ACanvas.Pixels[X - 3, Y - 1] := clBlack;
31      if (not Expanded) then
32      begin
33        ACanvas.Pixels[X - 1, Y + 1] := clBlack;
34        ACanvas.Pixels[X - 1, Y] := clBlack;
35        ACanvas.Pixels[X - 1, Y - 1] := clBlack;
36        ACanvas.Pixels[X - 1, Y - 2] := clBlack;
37        ACanvas.Pixels[X - 1, Y - 3] := clBlack;
38      end;
39    end;
40  
41    procedure DrawHorizLine(X, Y: Integer; HasButton: Boolean);
42    begin
43      if (HasButton) then
44        X := X + 5;
45      ACanvas.Pixels[X, Y] := clGray;
46      ACanvas.Pixels[X + 2, Y] := clGray;
47      ACanvas.Pixels[X + 4, Y] := clGray;
48    end;
49  
50    procedure DrawVertLine(X, Y0, Y1: Integer; HasButton: Boolean);
51    begin
52      if (HasButton) then
53        Y0 := Y0 + 5;
54      while (Y0 <= Y1) do
55      begin
56        ACanvas.Pixels[X, Y0] := clGray;
57        inc(Y0, 2);
58      end;
59    end;
60  
61    procedure TreeNodePaintTo(ATreeNode: TTreeNode; ACanvas: TCanvas);
62    var
63      FirstNode: Boolean;
64      CurNode: TTreeNode;
65      NewX, NewY, CurX, CurY, StateY, ImageY: Integer;
66    begin
67      CurNode := ATreeNode;
68      FirstNode := True;
69      while (CurNode <> nil) do
70      begin
71        if (not (CurNode.IsVisible or FullExpand)) then
72          Exit;
73        {Compute Start X and Y}
74        NewX := X + (CurNode.Level * OffsetX);
75        NewY := Y + (OffsetY div 2);
76        {Line to sibling node}
77        if (ATreeView.ShowLines) then
78        begin
79          if (not FirstNode) then
80          begin
81            if (ATreeView.ShowRoot or (CurNode.Level > 0)) then
82              DrawVertLine(NewX - 1, CurY - (OffsetY div 2) + 1, NewY, True);
83          end
84          else
85          begin
86            FirstNode := False;
87            {Line to parent node}
88            if (CurNode.Parent <> nil) then
89              DrawVertLine(NewX - 1, Y - (OffsetY div 2) + 1, NewY, True)
90          end;
91        end;
92        {Update Sibling offsets}
93        CurX := NewX;
94        CurY := NewY;
95        if (ATreeView.ShowRoot or (CurNode.Level > 0)) then
96        begin
97          if (ATreeView.ShowButtons) then
98          begin
99            {Draw the button}
100           if (CurNode.HasChildren) then
101           begin
102             DrawButton(NewX, NewY, FullExpand or CurNode.Expanded);
103             CurY := CurY + 9;
104           end;
105           if (ATreeView.ShowLines) then
106             DrawHorizLine(NewX, NewY, CurNode.HasChildren);
107         end
108         else if (ATreeView.ShowLines) then
109           DrawHorizLine(NewX, NewY, False);
110       end;
111       {Update X Offset}
112       NewX := NewX + 9;
113       {State Image}
114       if (Assigned(ATreeView.StateImages)) then
115       begin
116         {Draw the State Image}
117         StateY := Y + ((OffsetY - ATreeView.StateImages.Height) div 2);
118         ATreeView.StateImages.Draw(ACanvas, NewX, StateY, CurNode.StateIndex);
119         {Update X Offset}
120         NewX := NewX + ATreeView.StateImages.Width;
121       end;
122       {Image}
123       if (Assigned(ATreeView.Images)) then
124       begin
125         {Draw the Image}
126         ImageY := Y + ((OffsetY - ATreeView.Images.Height) div 2);
127         ATreeView.Images.Draw(ACanvas, NewX, ImageY, CurNode.ImageIndex);
128         {Update X Offset}
129         NewX := NewX + ATreeView.Images.Width;
130       end;
131       ACanvas.TextOut(NewX, Y, CurNode.Text);
132       {Update Y Offset}
133       Y := Y + OffsetY;
134       {Paint Child Nodes}
135       if (CurNode.GetFirstChild <> nil) then
136         TreeNodePaintTo(CurNode.GetFirstChild, ACanvas);
137       {Paint sibling nodes}
138       CurNode := CurNode.GetNextSibling;
139     end;
140   end;
141 begin
142   {Compute Offsets}
143   OffsetX := 19;
144   OffsetY := 5 * ACanvas.TextHeight('|') div 4;
145   if (Assigned(ATreeView.StateImages)) and (ATreeView.StateImages.Height > OffsetY)
146     then
147     OffsetY := ATreeView.StateImages.Height;
148   if (Assigned(ATreeView.Images)) and (ATreeView.Images.Height > OffsetY) then
149     OffsetY := ATreeView.Images.Height;
150   if (ATreeView.ShowRoot) then
151     X := X + 10;
152   TreeNodePaintTo(ATreeView.Items.GetFirstNode, ACanvas);
153 end;
154 
155 end.
			 |