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.
|