Sign in Join
Wiki copytemporary

VirtualTreeview Example for Lazarus: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: VirtualTreeview Example for Lazarus

OldNewLine
1+Here are a few examples on how to use [[VirtualTreeview]] for Lazarus (tested on win32). These are mostly collected from the web written for Delphi, and from the tutorial/docs by Philipp Frenzel and Mike Lischke.
2+
3+The tutorial/docs can be downloaded from <http://www.soft-gems.net>. Below someone would find only the quick way to use VirtualTreeview on Lazarus, not explanations. For explanations and lots of other functions/methods, get the official documents and the tutorial.
4+
5+# Basic Tree Listview With 3 Columns
6+
7+1\. Run lazarus. In Lazarus v2.0 or newer, the VirtualTreeViews package is installed by default. In all other cases, or if you want to work with a different version, install the component as explained in [[VirtualTreeView|VirtualTreeView#Installation]].
8+
9+2\. Drop a TVirtualStringTree component (under Virtual Controls tab). Note that in Lazarus v2.2+ the built-in component is named TLazVirtualStringTree and is found on the LazControls tab.
10+
11+3\. Go to the Source Editor (press F12) and check that a unit VirtualTrees (in Laz 2.2+: laz.VirtualTrees) has been added to the uses clause:
12+
13+``` pascal
14+uses
15+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
16+ VirtualTrees; // or laz.VirtualTrees in Laz 2.2+
17+```
18+
19+4\. Get to the Form Designer (press F12). Select the Virtual Tree Component. On the Object Inspector click on Name, type VST and press enter. Click on Header (expand it) -\> Columns, click on the small button next to "0 items". Click 3 times on Add button to add 3 columns. Do not close this window.
20+
21+5\. On the Column Editing window now the 3rd column is selected. Get to the Object Inspector. Click on Options (expand it) -\> set coAllowClick to False.
22+
23+6\. Click on Text. Type Column2.
24+
25+7\. Click on Width, type 100 and press enter.
26+
27+8\. Get to the Column Editing window, select 1st and 2nd column, and set their property as above (for Text field, use different names, ie Column0, Column1).
28+
29+9\. Close the Column Editing window. Select the Virtual Tree Component on form. On Object Inspector get to Header -\> Options (expand). Set hoVisible to True.
30+
31+10\. Scroll down to Style set it to hsFlatButtons.
32+
33+11\. Scroll down to TreeOptions(expand) -\> MiscOption(expand), set toEditable to True. Set toGridExtensions to True.
34+
35+12\. Scroll down to SelectionOptions(expand) -\> set toExtendedFocus to True. Set toMultiSelect to True. On the Form Designer resize VST (Virtual Tree Component) to get view of all the columns, if needed.
36+
37+13\. Now to add 3 buttons on the form. Get these from the component palette - Standard Tab ("OK" Label).
38+
39+14\. Click on Button1, on Object Inspector change Caption to AddRoot. Click on Button2, change the caption to AddChild. Change the caption of Button3 to Delete.
40+
41+15\. Keep this here and get to the Source Editor (press F12). On Source Editor replace the line:
42+
43+{\$mode objfpc}{\$H+} with {\$MODE DELPHI}
44+
45+16\. Under the "implementation" paste following lines:
46+
47+``` pascal
48+type
49+ PTreeData = ^TTreeData;
50+ TTreeData = record
51+ Column0: String;
52+ Column1: String;
53+ Column2: String;
54+ end;
55+```
56+
57+17\. Get to the Form Designer (press F12). Select VST. Go to Object Inspector, select Events tab, scroll down to onChange. Double click on the combobox. Paste the follwings:
58+
59+``` pascal
60+procedure TForm1.VSTChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
61+begin
62+ VST.Refresh;
63+end;
64+```
65+
66+18\. Scroll to onFocusChanged. Double click and paste the following:
67+
68+``` pascal
69+procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
70+ Column: TColumnIndex);
71+begin
72+ VST.Refresh;
73+end;
74+```
75+
76+19\. Scroll to onFreeNode. Double click and paste the following:
77+
78+``` pascal
79+procedure TForm1.VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
80+var
81+ Data: PTreeData;
82+begin
83+ Data := VST.GetNodeData(Node);
84+ if Assigned(Data) then begin
85+ Data^.Column0 := '';
86+ Data^.Column1 := '';
87+ Data^.Column2 := '';
88+ end;
89+end;
90+```
91+
92+20\. Scroll down to onGetNodeDataSize. Double click & paste the following:
93+
94+``` pascal
95+procedure TForm1.VSTGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
96+begin
97+ NodeDataSize := SizeOf(TTreeData);
98+end;
99+```
100+
101+21\. Scroll to onGetText. Double click & paste the following:
102+
103+``` pascal
104+procedure TForm1.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
105+ Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
106+var
107+ Data: PTreeData;
108+begin
109+ Data := VST.GetNodeData(Node);
110+ case Column of
111+ 0: CellText := Data^.Column0;
112+ 1: CellText := Data^.Column1;
113+ 2: CellText := Data^.Column2;
114+ end;
115+end;
116+```
117+
118+22\. Press F12 to get Form Designer. Double click AddRoot button. Paste the following:
119+
120+``` pascal
121+procedure TForm1.Button1Click(Sender: TObject);
122+var
123+ Data: PTreeData;
124+ XNode: PVirtualNode;
125+ Rand: Integer;
126+begin
127+ Randomize;
128+ Rand := Random(99);
129+ XNode := VST.AddChild(nil);
130+
131+ if VST.AbsoluteIndex(XNode) > -1 then
132+ begin
133+ Data := VST.GetNodeData(Xnode);
134+ Data^.Column0 := 'One ' + IntToStr(Rand);
135+ Data^.Column1 := 'Two ' + IntToStr(Rand + 10);
136+ Data^.Column2 := 'Three ' + IntToStr(Rand - 10);
137+ end;
138+end;
139+```
140+
141+23\. Press F9 to Run the project to check. Click on AddRoot to add node. If it is ok, node will be added on VST.
142+
143+24\. Stop execution. On the Form Designer double click the AddChild captioned button. Paste the following:
144+
145+``` pascal
146+procedure TForm1.Button2Click(Sender: TObject);
147+var
148+ XNode: PVirtualNode;
149+ Data: PTreeData;
150+begin
151+ if not Assigned(VST.FocusedNode) then
152+ Exit;
153+
154+ XNode := VST.AddChild(VST.FocusedNode);
155+ Data := VST.GetNodeData(Xnode);
156+
157+ Data^.Column0 := 'Ch 1';
158+ Data^.Column1 := 'Ch 2';
159+ Data^.Column2 := 'Ch 3';
160+
161+ VST.Expanded[VST.FocusedNode] := True;
162+end;
163+```
164+
165+25\. On the Form Designer double click the Delete captioned button. Paste the following:
166+
167+``` pascal
168+procedure TForm1.Button3Click(Sender: TObject);
169+begin
170+ VST.DeleteSelectedNodes;
171+end;
172+```
173+
174+26\. Run the project by pressing F9 to check. Add some node, child and delete them.
175+
176+27\. Try to edit a node. Select a node and press F2, write new value. If you can see what you are typing then it is OK. Else, read below "If Cell Editing Can't Be Seen".
177+
178+28\. To get VST show the new value entered after editing, go to the Form Designer, select VST. On Object Inspector -\> Events -\> OnNewText combobox double click. Paste the following:
179+
180+``` pascal
181+procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
182+ Column: TColumnIndex; NewText: WideString);
183+var
184+ Data: PTreeData;
185+begin
186+ Data := VST.GetNodeData(Node);
187+ case Column of
188+ 0: Data^.Column0 := NewText;
189+ 1: Data^.Column1 := NewText;
190+ 2: Data^.Column2 := NewText;
191+ end;
192+end;
193+```
194+
195+So far, example for basic use ends here. You may drop few more buttons on the form to test few more commands given below. Next would be, showing checkbox, image, font colour and adding combobox on node.
196+
197+- Another Way to Add Root Node
198+
199+``` pascal
200+procedure TForm1.Button8Click(Sender: TObject);
201+begin
202+ with VST do
203+ RootNodeCount := RootNodeCount + 1;
204+end;
205+```
206+
207+- Another Way to Add Child
208+
209+``` pascal
210+procedure TForm1.Button9Click(Sender: TObject);
211+begin
212+ if Assigned(VST.FocusedNode) then
213+ VST.ChildCount[VST.FocusedNode] := VST.ChildCount[VST.FocusedNode] + 1;
214+end;
215+```
216+
217+- Determine & Delete Children of A Node
218+
219+``` pascal
220+procedure TForm1.Button4Click(Sender: TObject);
221+var
222+ c: Integer;
223+begin
224+ if not Assigned(VST.FocusedNode) then
225+ Exit;
226+ if VST.HasChildren[VST.FocusedNode] then
227+ begin
228+ c := VST.ChildCount[VST.FocusedNode];
229+ VST.DeleteChildren(VST.FocusedNode);
230+ ShowMessage('Number of deleted child:' + #13#10 + IntToStr(c));
231+ end;
232+end;
233+```
234+
235+- Delete Node
236+
237+``` pascal
238+procedure TForm1.Button5Click(Sender: TObject);
239+begin
240+{VST.Clear; //Delete All Nodes}
241+ if not Assigned(VST.FocusedNode) then
242+ Exit;
243+ VST.DeleteNode(VST.FocusedNode);
244+end;
245+```
246+
247+- Search and Select Node
248+
249+``` pascal
250+procedure TForm1.Button6Click(Sender: TObject);
251+var
252+ XNode: PVirtualNode;
253+ Data: PTreeData;
254+begin
255+ XNode := VST.GetFirst;
256+
257+ while XNode <> nil do
258+ begin
259+ Data := VST.GetNodeData(XNode);
260+ if Data^.Column0 = '1' then
261+ begin
262+ VST.ClearSelection;
263+ VST.Selected[XNode] := True;
264+ VST.SetFocus;
265+ break;
266+ end else
267+ XNode := VST.GetNextSibling(XNode);
268+ end;
269+end;
270+```
271+
272+- Determine Parent
273+
274+``` pascal
275+procedure TForm1.Button13Click(Sender: TObject);
276+var
277+ XNode: PVirtualNode;
278+begin
279+ if not Assigned(VST.FocusedNode) then
280+ Exit;
281+ XNode := VST.FocusedNode;
282+ while VST.GetNodeLevel(XNode) > 0 do
283+ begin
284+ XNode := XNode.Parent;
285+ VST.Selected[XNode] := True;
286+ end;
287+ VST.Refresh;
288+ VST.SetFocus;
289+end;
290+```
291+
292+- Search All
293+
294+``` pascal
295+procedure TForm1.Button7Click(Sender: TObject);
296+var
297+ XNode: PVirtualNode;
298+ Data: PTreeData;
299+begin
300+ if VST.GetFirst = nil then Exit;
301+ XNode := nil;
302+ repeat
303+ if XNode = nil then
304+ XNode := VST.GetFirst
305+ else
306+ XNode := VST.GetNext(XNode);
307+ Data := VST.GetNodeData(XNode);
308+ if (Data^.Column0 = '1') or (Data^.Column1 = '1') or (Data^.Column2 = '1') then
309+ begin
310+ ShowMessage('Found at Node Level : ' + IntToStr(VST.GetNodeLevel(XNode)));
311+ break;
312+ end;
313+ until XNode = VST.GetLast();
314+end;
315+```
316+
317+- Search next
318+
319+``` pascal
320+procedure TForm1.Button8Click(Sender: TObject);
321+var
322+ XNode: PVirtualNode;
323+ Data: PTreeData;
324+begin
325+ if not Assigned(VST.GetFirst) then
326+ Exit
327+ else
328+ XNode := VST.GetFirst;
329+ repeat
330+ XNode := VST.GetNext(XNode);
331+ Data := VST.GetNodeData(XNode);
332+ if Pos(LowerCase(SearchEdit.Text), LowerCase(Data^.Column0)) > 0 then
333+ begin
334+ VST.FocusedNode := XNode;
335+ VST.Selected[XNode] := True;
336+ if MessageDlg('Item found?', mtConfirmation, mbYesNo, 0) = mrYes then
337+ begin
338+ VST.Expanded[XNode] := True;
339+ VST.Refresh;
340+ VST.SetFocus;
341+ Break;
342+ end;
343+ end;
344+ until XNode = VST.GetLast;
345+end;
346+```
347+
348+- Insert Node
349+
350+``` pascal
351+procedure TForm1.Button12Click(Sender: TObject);
352+var
353+ XNode: PVirtualNode;
354+begin
355+ if Assigned(VST.FocusedNode) then
356+ begin
357+ XNode := VST.InsertNode(VST.FocusedNode,amInsertBefore);
358+ // To Insert After Selected Node.
359+ {XNode := VST.InsertNode(VST.FocusedNode,amInsertAfter);}
360+ VST.Refresh;
361+ end;
362+end;
363+```
364+
365+- Set Node Height
366+
367+``` pascal
368+procedure TForm1.Button14Click(Sender: TObject);
369+begin
370+ if Assigned(VST.FocusedNode) then
371+ VST.NodeHeight[VST.FocusedNode] := 32;
372+end;
373+```
374+
375+- Save And Load
376+
377+Simple tree (without column) can be saved and loaded as:
378+
379+``` pascal
380+VST.SaveToFile('filename.dat');
381+VST.LoadFromFile('filename.dat');
382+```
383+
384+To save and load the above mentioned example, place 2 buttons on the form, rename caption to "Save" and "Load". Select VST, on Object Inspector -\> TreeOptions -\> StringOptions make sure toSaveCaptions is set to True. Go to Object Inspector's Events tab. Scroll down to OnLoadNode, double click and then paste:
385+
386+``` pascal
387+procedure TForm1.VSTLoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
388+ Stream: TStream);
389+var
390+ Data: PTreeData;
391+ Len: Integer;
392+begin
393+ Data := VST.GetNodeData(Node);
394+ Stream.read(Len, SizeOf(Len));
395+ SetLength(Data^.Column0, Len);
396+ Stream.read(PChar(Data^.Column0)^, Len);
397+
398+ Stream.read(Len, SizeOf(Len));
399+ SetLength(Data^.Column1, Len);
400+ Stream.read(PChar(Data^.Column1)^, Len);
401+
402+ Stream.read(Len, SizeOf(Len));
403+ SetLength(Data^.Column2, Len);
404+ Stream.read(PChar(Data^.Column2)^, Len);
405+end;
406+```
407+
408+Again on Object Inspector's Events tab - scroll down to OnSaveNode, double click and then paste:
409+
410+``` pascal
411+procedure TForm1.VSTSaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
412+ Stream: TStream);
413+var
414+ Data: PTreeData;
415+ Len: Integer;
416+begin
417+ Data := VST.GetNodeData(Node);
418+ Len := Length(Data^.Column0);
419+ Stream.write(Len, SizeOf(Len));
420+ Stream.write(PChar(Data^.Column0)^, Len);
421+
422+ Len := Length(Data^.Column1);
423+ Stream.write(Len, SizeOf(Len));
424+ Stream.write(PChar(Data^.Column1)^, Len);
425+
426+ Len := Length(Data^.Column2);
427+ Stream.write(Len, SizeOf(Len));
428+ Stream.write(PChar(Data^.Column2)^, Len);
429+end;
430+```
431+
432+On Form Designer double click Save captioned button, paste:
433+
434+``` pascal
435+procedure TForm1.Button10Click(Sender: TObject);
436+begin
437+ VST.SaveToFile('C:\vst.dat');
438+end;
439+```
440+
441+On Form Designer double click Load captioned button, paste:
442+
443+``` pascal
444+procedure TForm1.Button11Click(Sender: TObject);
445+begin
446+ VST.LoadFromFile('C:\vst.dat');
447+end;
448+```
449+
450+Now test to save and load tree.
451+
452+- Scroll Problem
453+
454+The header of the treeview disappears fully or partially when scrolling. I could not find a good solution for this. One way to overcome this is to set the header height to 0, then using general label for columns. This is good while there are few columns, and all are visible without horizontal scrolling. Or, the header height can be set to a higher value like 25 or 30. VST.Refresh can be added to the OnScroll event.
455+
456+- Column Resize
457+
458+It was not possible to resize column by dragging mouse on VST header. May be it is for the header bug or I have missed something. If it is for the header, probably will be fixed on next version of Lazarus. See this link: <http://bugs.freepascal.org/view.php?id=11209>\
459+Anyway, it is possible to resize column from code. When you press down right mouse button and move mouse-wheel up, the width of the selected column increases and press down the right mouse button and move mouse-wheel down, to decrease the width of the selected column. To do this:
460+
461+1\. Add a variable in the source editor named as CurCol: Integer; So it looks like:
462+
463+``` pascal
464+var
465+ Form1: TForm1;
466+ CurCol: Integer; // <- Add this line only.
467+
468+implementation
469+
470+{ TForm1 }
471+```
472+
473+2\. On the Form Designer double click the form to generate an event OnCreate. Inside the OnCreate procedure type Form1.OnMouseWheelUp:= and press Ctrl+Shift+C, this will complete the code and make skeleton of the MouseWheelUp event. Now get back to procedure TForm1.FormCreate(Sender: TObject); And add another event for MouseWheelDown. Type Form1.OnMouseWheelDown:= and press Ctrl+Shift+C, to generate the MouseWheelDown event. FormCreate procedure now looks like:
474+
475+``` pascal
476+procedure TForm1.FormCreate(Sender: TObject);
477+begin
478+ Form1.OnMouseWheelUp := @Form1MouseWheelUp;
479+ Form1.OnMouseWheelDown := @Form1MouseWheelDown;
480+end;
481+```
482+
483+3\. Fill the TForm1.Form1MouseWheelUp procedure as:
484+
485+``` pascal
486+procedure TForm1.Form1MouseWheelUp(Sender: TObject; Shift: TShiftState;
487+ MousePos: TPoint; var Handled: Boolean);
488+begin
489+ if VST.Focused and (ssRight in Shift) then
490+ VST.Header.Columns[CurCol].Width := VST.Header.Columns[CurCol].Width + 10;
491+end;
492+```
493+
494+4\. Fill the TForm1.Form1MouseWheelDown procedure as:
495+
496+``` pascal
497+procedure TForm1.Form1MouseWheelDown(Sender: TObject; Shift: TShiftState;
498+ MousePos: TPoint; var Handled: Boolean);
499+begin
500+ if VST.Focused and (ssRight in Shift) then
501+ VST.Header.Columns[CurCol].Width := VST.Header.Columns[CurCol].Width - 10;
502+end;
503+```
504+
505+5\. Go to the Form Designer (press F12), select VST, on Object Inspector's Events tab scroll to OnFocusChanged, double click & paste:
506+
507+``` pascal
508+procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree;
509+ Node: PVirtualNode; Column: TColumnIndex);
510+begin
511+ CurCol := Column;
512+end;
513+```
514+
515+When you run, click on a column then hold down right mouse button and move wheel up to increase width, wheel down to decrease. You may tune-up the above procedures if needed. Or, add keyboard event with something like "if (key=187) and (ssShift in Shift) then" to watch for Shift + "+".
516+
517+# Checkbox
518+
519+On Form Designer select VST. Go to:
520+
521+1. Object Inspector -\> Properties -\> CheckImageKind and select ckDarkCheck.
522+2. Object Inspector -\> Properties -\> TreeOptions -\> MiscOptions -\> toCheckSupport and set it to True.
523+
524+Now switch to Events tab.
525+
526+- Scroll to OnInitNode. Double click and paste the following:
527+
528+``` pascal
529+procedure TForm1.VSTInitNode(Sender: TBaseVirtualTree; ParentNode,
530+ Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
531+var
532+ Level: Integer;
533+begin
534+ Level := VST.GetNodeLevel(Node);
535+ if Level = 0 then
536+ Node.CheckType := ctCheckBox;
537+
538+ if Level = 2 then
539+ Node.CheckType := ctRadioButton;
540+
541+ if Level = 1 then
542+ begin
543+ Node.CheckType := ctTriStateCheckBox;
544+ Node.CheckState := csCheckedNormal;
545+ end;
546+
547+ if Level = 3 then
548+ Node.CheckType := ctButton;
549+end;
550+```
551+
552+Run the program, add rootnode and child then child of the child, and check if you can check and uncheck properly. If not, close the program. Go to the Object Inspector's Events tab.
553+
554+- Scroll to OnChecked, double click and paste:
555+
556+``` pascal
557+procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
558+begin
559+ VST.Refresh;
560+end;
561+```
562+
563+- Scroll to OnChecking, double click and paste:
564+
565+``` pascal
566+procedure TForm1.VSTChecking(Sender: TBaseVirtualTree; Node: PVirtualNode;
567+ var NewState: TCheckState; var Allowed: Boolean);
568+begin
569+ VST.Refresh;
570+end;
571+```
572+
573+Hope now it is ok. To determine checkbox state use like:
574+
575+``` pascal
576+if XNode.CheckState = csCheckedNormal then
577+ShowMessage('Checked.');
578+```
579+
580+Other states are:
581+
582+``` pascal
583+csUncheckedNormal = unchecked and not pressed
584+csUncheckedPressed = unchecked and pressed
585+csCheckedNormal = checked and not pressed
586+csCheckedPressed = checked and pressed
587+csMixedNormal = 3-state check box and not pressed
588+csMixedPressed = 3-state check box and pressed
589+```
590+
591+------------------------------------------------------------------------
592+
593+Note:visually, the `CheckState` property looks something like this:\
594+![](/wiki-images/1/11/Checkstate_enum.png)\
595+----
596+
597+Other types are:
598+
599+``` pascal
600+ctNone
601+ctTriStateCheckBox
602+ctCheckBox
603+ctRadioButton
604+ctButton
605+```
606+
607+------------------------------------------------------------------------
608+
609+Note: visually, the `CheckType` property looks something like this:\
610+![](/wiki-images/a/ab/Checktype_enum.png)\
611+----
612+
613+- To Catch Checkbox's Button (ctButton) Click
614+
615+Go to Object Inspector's Events tab. Scroll to OnChecked, double click and paste:
616+
617+``` pascal
618+procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
619+begin
620+ if Node.CheckType = ctButton then
621+ ShowMessage('Ok.');
622+ VST.Refresh;
623+end;
624+```
625+
626+End of checkbox.\
627+
628+# Images
629+
630+To show image on VST nodes, a list of image should be created.
631+
632+- Go to the Component Palette -\> Common Controls. Select and drop a TImageList component on the form. Right click on the component icon and select ImageList Editor. Click on Add button and and select some images (at least 3 for now), then click on tick button to accept and close the ImageList Editor. By the way, there are some nice images you can download from <http://www.famfamfam.com/lab/icons/silk/>
633+
634+- Now on the Form Designer select VST, and on Object Inspector's Properties tab, scroll to Images and select ImageList1
635+
636+- On Object Inspector's Events tab scroll to OnGetImageIndex, double click and paste:
637+
638+``` pascal
639+procedure TForm1.VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
640+ Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean;
641+ var ImageIndex: Integer);
642+begin
643+ if Kind in [ikNormal , ikSelected] then // Either Selected or not
644+ begin
645+ if Column = 0 then // if 1st Column
646+ ImageIndex := 0; // 1st Image of the ImageList1
647+
648+ if Column = 1 then // if 2nd Column
649+ ImageIndex := 1; // 2nd Image of the ImageList1
650+
651+ if Sender.FocusedNode = Node then // Only show if Focused
652+ if Column = 2 then // if 3rd Column
653+ ImageIndex := 2; // 3rd Image of the ImageList1
654+ end;
655+ {Sender.NodeHeight[node] := 40; //If Image is big}
656+end;
657+```
658+
659+# Font Colour
660+
661+On the Form Designer select VST, and on Object Inspector's Events tab, scroll to OnPaintText, double click and paste:
662+
663+``` pascal
664+procedure TForm1.VSTPaintText(Sender: TBaseVirtualTree;
665+ const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
666+ TextType: TVSTTextType);
667+var
668+ Data: PTreeData;
669+begin
670+ Data := VST.GetNodeData(Node);
671+
672+ if Data^.Column0 = 'sky' then
673+ TargetCanvas.Font.Color := clBlue;
674+
675+ if Column = 1 then
676+ begin
677+ TargetCanvas.Font.Color := clRed;
678+ TargetCanvas.Font.Style := Font.Style + [fsItalic];
679+ end;
680+
681+ if Column = 2 then
682+ begin
683+// ImageList1.Draw(Form1.Canvas, -1, -1, 2); {draw top left of form, 3rd image of ImageList1??}
684+ TargetCanvas.Font.Size := 9;
685+ TargetCanvas.Font.Color := clHighlightText;
686+ end;
687+end;
688+```
689+
690+# Adding A Combobox
691+
692+- I guess you have an open project on Lazarus IDE having VST on it and can edit the nodes. If not see the "Basic Tree Listview With 3 Columns" above, and atleast complete steps 1 to 21.
693+
694+- Below there is an unit file named combo. Copy that unit and save as combo.pas inside the project directory. Under your program's uses clause add combo. So it may look like:
695+
696+``` pascal
697+uses
698+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
699+ VirtualStringTree, VirtualTrees, combo;
700+```
701+
702+- The combo.pas unit
703+
704+``` pascal
705+unit combo;
706+
707+{$mode delphi}
708+
709+interface
710+
711+uses
712+ Classes, SysUtils, LResources, LMessage, LCLType, Forms, Controls, Graphics, Dialogs,
713+ VirtualStringTree, VirtualTrees, StdCtrls, windows;
714+
715+type
716+ TStringEditLink = class(TInterfacedObject, IVTEditLink)
717+ private
718+ FEdit: TWinControl;
719+ FTree: TVirtualStringTree;
720+ FNode: PVirtualNode;
721+ FColumn: Integer;
722+ protected
723+ procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
724+ public
725+ destructor Destroy; override;
726+ function BeginEdit: Boolean; stdcall;
727+ function CancelEdit: Boolean; stdcall;
728+ function EndEdit: Boolean; stdcall;
729+ function GetBounds: TRect; stdcall;
730+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
731+ procedure ProcessMessage(var Message: TLMessage); stdcall;
732+ procedure SetBounds(R: TRect); stdcall;
733+ end;
734+
735+implementation
736+
737+destructor TStringEditLink.Destroy;
738+begin
739+ FEdit.Free;
740+ inherited;
741+end;
742+
743+procedure TStringEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
744+begin
745+ case Key of
746+ VK_ESCAPE:
747+ begin
748+ FTree.CancelEditNode;
749+ Key := 0;
750+ FTree.setfocus;
751+ end;
752+ VK_RETURN:
753+ begin
754+ PostMessage(FTree.Handle, WM_KEYDOWN, VK_DOWN, 0);
755+ Key := 0;
756+ FTree.EndEditNode;
757+ FTree.setfocus;
758+ end;
759+ end; //case
760+end;
761+
762+function TStringEditLink.BeginEdit: Boolean;
763+begin
764+ Result := True;
765+ //FEdit.Height := (FTree.DefaultNodeHeight - 1); //Needed for editbox. Not combo
766+ FEdit.Show;
767+ TComboBox(FEdit).DroppedDown := True;
768+ FEdit.SetFocus;
769+end;
770+
771+function TStringEditLink.CancelEdit: Boolean;
772+begin
773+ Result := True;
774+ FEdit.Hide;
775+end;
776+
777+function TStringEditLink.EndEdit: Boolean;
778+var
779+ s: String;
780+begin
781+ Result := True;
782+ s := TComboBox(FEdit).Text;
783+ FTree.Text[FNode, FColumn] := s;
784+
785+ FTree.InvalidateNode(FNode);
786+ FEdit.Hide;
787+ FTree.SetFocus;
788+end;
789+
790+function TStringEditLink.GetBounds: TRect;
791+begin
792+ Result := FEdit.BoundsRect;
793+end;
794+
795+function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
796+begin
797+ Result := True;
798+ FTree := Tree as TVirtualStringTree;
799+ FNode := Node;
800+ FColumn := Column;
801+
802+ FEdit.Free;
803+ FEdit := nil;
804+
805+ FEdit := TComboBox.Create(nil);
806+ with FEdit as TComboBox do
807+ begin
808+ Visible := False;
809+ Parent := Tree;
810+ Items.Add('Google');
811+ Items.Add('Yahoo');
812+ Items.Add('Altavista');
813+ OnKeyDown := EditKeyDown;
814+ end;
815+end;
816+
817+procedure TStringEditLink.ProcessMessage(var Message: TLMessage);
818+begin
819+ FEdit.WindowProc(Message);
820+end;
821+
822+procedure TStringEditLink.SetBounds(R: TRect);
823+var
824+ Dummy: Integer;
825+begin
826+ FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
827+ FEdit.BoundsRect := R;
828+end;
829+
830+end.
831+```
832+
833+- After saving the file, on the Form Designer select VST and on Object Inspector's Properties, scroll to TreeOptions -\> MiscOptions, set toEditable to True. Then get to TreeOptions -\> SelectionOptions, set toExtendedFocus to True.
834+
835+- Switch to Object Inspector's Events tab. Scroll to OnCreateEditor, double click and paste:
836+
837+``` pascal
838+procedure TForm1.VSTCreateEditor(Sender: TBaseVirtualTree;
839+ Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
840+begin
841+ EditLink := TStringEditLink.Create;
842+end;
843+```
844+
845+- On Object Inspector's Events tab. Scroll to OnNewText, double click and paste:
846+
847+``` pascal
848+procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
849+ Column: TColumnIndex; NewText: WideString);
850+var
851+ Data: PTreeData;
852+begin
853+ Data := VST.GetNodeData(Node);
854+ case Column of
855+ 0: Data^.Column0 := NewText;
856+ 1: Data^.Column1 := NewText;
857+ 2: Data^.Column2 := NewText;
858+ end;
859+end;
860+```
861+
862+Run program, select a node and press F2 to get combobox. On pressing Enter new value should appear on the node.
863+
864+# If Cell Editing Can't Be Seen
865+
866+Open VirtualStringTree.pas unit file (if you are still on the above example project, right click on VirtualStringTree under Uses clause and select Find Declaration. This opens the file on next tab. Go to that file's tab.). Get to the "function TStringEditLink.BeginEdit: Boolean; stdcall;". It looks like:
867+
868+``` pascal
869+function TStringEditLink.BeginEdit: Boolean; stdcall;
870+
871+// Notifies the edit link that editing can start now. Descentants may cancel node edit
872+// by returning False.
873+
874+begin
875+ Result := not FStopping;
876+ if Result then
877+ begin
878+ FEdit.Show;
879+ FEdit.SelectAll;
880+ FEdit.SetFocus;
881+ end;
882+end;
883+```
884+
885+Now add "FEdit.Height:=18;". It should look like:
886+
887+``` pascal
888+function TStringEditLink.BeginEdit: Boolean; stdcall;
889+
890+// Notifies the edit link that editing can start now. Descentants may cancel node edit
891+// by returning False.
892+
893+begin
894+ Result := not FStopping;
895+ if Result then
896+ begin
897+ FEdit.Show;
898+ FEdit.SelectAll;
899+ FEdit.SetFocus;
900+ FEdit.Height := 18; // <--- Added this line.
901+ end;
902+end;
903+```
904+
905+Save the file (press Ctrl + S). If you are on the example project, close this (Project -\> Close Project). Click on Tools -\> Configure "Build Lazarus" ... Select Clean Up + Build All and then click on the Build button. After compile Lazarus should be restarted. Now open the example project and try to edit node on VST. This time it should be ok.
906+
907+# External links
908+
909+- [Virtual Treeview tutorials/docs](http://www.soft-gems.net)
910+- [Programando en Pascal](http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_Pascal) - Spanish tutorial focused on FPC/Lazarus, hosted in Wikibooks.
911+
912+---
913+*From the Free Pascal wiki, page "VirtualTreeview Example for Lazarus", as it stood on 2023-02-27 after a change by Kepsz, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history