Difference between revisions of "VirtualTreeview Example for Lazarus"

From Lazarus wiki
Jump to navigationJump to search
m (Saddly, the WM_KEYDOWN is still requires the windows unit.)
 
(29 intermediate revisions by 16 users not shown)
Line 1: Line 1:
 
{{Example for VirtualTreeview on Lazarus}}
 
{{Example for VirtualTreeview on Lazarus}}
  
Here are 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. The tutorial/docs can be downloaded from http://www.soft-gems.net . Bellow someone would find only the quick way to use VirtualTreeview on Lazarus, not explanation. For explanation and lots of other functions/methods get the official documents and the tutorial.
+
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.  
 +
 
 +
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.
  
 
=Basic Tree Listview With 3 Columns=
 
=Basic Tree Listview With 3 Columns=
  
1. Install the component. Run lazarus.
+
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#Installation]].  
  
2. Drop a TVirtualStringTree component (under Virtual Controls tab).
+
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.
  
3. Go to the Source Editor (press F12). Under Uses clause add a unit - named VirtualTrees (if it is not already there and this is not the VirtualStringTree). So it may look like:
+
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:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
uses
 
uses
 
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
 
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
   VirtualStringTree, VirtualTrees;  
+
   VirtualTrees; // or laz.VirtualTrees in Laz 2.2+
</PRE>   
+
</syntaxhighlight>   
 +
 
 
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.
 
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.
  
5. On the Column Editing window now the 3rd column is selected. Get to the Object Inspector. Click on Options (expand it) -> set coAllowClicks to False.
+
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.
  
 
6. Click on Text. Type Column2.
 
6. Click on Text. Type Column2.
Line 43: Line 47:
 
16. Under the "implementation" paste following lines:
 
16. Under the "implementation" paste following lines:
  
<PRE>
+
<syntaxhighlight lang=pascal>
 
type
 
type
 
   PTreeData = ^TTreeData;
 
   PTreeData = ^TTreeData;
Line 51: Line 55:
 
     Column2: String;
 
     Column2: String;
 
   end;
 
   end;
</PRE>
+
</syntaxhighlight>
  
 
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:
 
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:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
procedure TForm1.VSTChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
begin
 
begin
VST.Refresh;
+
  VST.Refresh;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 +
18. Scroll to onFocusChanged. Double click and paste the following:
  
18. Scroll to onFocusChanged. Double click and paste the follwings:
+
<syntaxhighlight lang=pascal>
<PRE>
 
 
procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
   Column: TColumnIndex);
 
   Column: TColumnIndex);
 
begin
 
begin
 
   VST.Refresh;
 
   VST.Refresh;
end;  
+
end;
</PRE>
+
</syntaxhighlight>
 
    
 
    
19. Scroll to onFreeNode. Double click and paste the follwings:
+
19. Scroll to onFreeNode. Double click and paste the following:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
procedure TForm1.VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
var
 
var
Data: PTreeData;
+
  Data: PTreeData;
 
begin
 
begin
Data:=VST.GetNodeData(Node);
+
  Data := VST.GetNodeData(Node);
if Assigned(Data) then begin
+
  if Assigned(Data) then begin
  Data^.Column0 := '';
+
    Data^.Column0 := '';
  Data^.Column1 := '';
+
    Data^.Column1 := '';
  Data^.Column2 := '';
+
    Data^.Column2 := '';
end;
+
  end;
end;
+
end;
</PRE>
+
</syntaxhighlight>
 +
 
 +
20. Scroll down to onGetNodeDataSize. Double click & paste the following:
  
20. Scroll down to onGetNodeDataSize. Double click & paste the follwings:
+
<syntaxhighlight lang=pascal>
<PRE>
 
 
procedure TForm1.VSTGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
 
procedure TForm1.VSTGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
 
begin
 
begin
    NodeDataSize := SizeOf(TTreeData);
+
  NodeDataSize := SizeOf(TTreeData);
end;  
+
end;
</PRE>
+
</syntaxhighlight>
 +
 
 +
21. Scroll to onGetText. Double click & paste the following:
  
21. Scroll to onGetText. Double click & paste the follwings:
+
<syntaxhighlight lang=pascal>
<PRE>
 
 
procedure TForm1.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
 
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
Line 106: Line 115:
 
     2: CellText := Data^.Column2;
 
     2: CellText := Data^.Column2;
 
   end;
 
   end;
end;  
+
end;
</PRE>
+
</syntaxhighlight>
  
22. Press F12 to get Form Designer. Double click AddRoot button. Paste the followings:
+
22. Press F12 to get Form Designer. Double click AddRoot button. Paste the following:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
Var
+
var
Data: PTreeData;
+
  Data: PTreeData;
XNode: PVirtualNode;
+
  XNode: PVirtualNode;
Rand: Integer;
+
  Rand: Integer;
Begin
+
begin
 
   Randomize;
 
   Randomize;
 
   Rand := Random(99);
 
   Rand := Random(99);
   XNode:=VST.AddChild(nil);
+
   XNode := VST.AddChild(nil);
 
    
 
    
 
   if VST.AbsoluteIndex(XNode) > -1 then
 
   if VST.AbsoluteIndex(XNode) > -1 then
   Begin
+
   begin
  Data := VST.GetNodeData(Xnode);
+
    Data := VST.GetNodeData(Xnode);
  Data^.Column0:= 'One ' + IntToStr(Rand);
+
    Data^.Column0 := 'One ' + IntToStr(Rand);
  Data^.Column1:= 'Two ' + IntToStr(Rand + 10);
+
    Data^.Column1 := 'Two ' + IntToStr(Rand + 10);
  Data^.Column2:= 'Three ' + IntToStr(Rand - 10);
+
    Data^.Column2 := 'Three ' + IntToStr(Rand - 10);
   End;  
+
   end;  
End;  
+
end;
</PRE>
+
</syntaxhighlight>
  
 
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.
 
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.
  
24. Stop execution. On the Form Designer double click the AddChild captioned button. Paste the followings:
+
24. Stop execution. On the Form Designer double click the AddChild captioned button. Paste the following:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button2Click(Sender: TObject);
 
procedure TForm1.Button2Click(Sender: TObject);
 
var
 
var
Line 143: Line 154:
 
     Exit;
 
     Exit;
 
      
 
      
    XNode := VST.AddChild(VST.FocusedNode);
+
  XNode := VST.AddChild(VST.FocusedNode);
    Data := VST.GetNodeData(Xnode);
+
  Data := VST.GetNodeData(Xnode);
 
      
 
      
    Data^.Column0:= 'Ch 1';
+
  Data^.Column0 := 'Ch 1';
    Data^.Column1:= 'Ch 2';
+
  Data^.Column1 := 'Ch 2';
    Data^.Column2:= 'Ch 3';
+
  Data^.Column2 := 'Ch 3';
  
  VST.Expanded[VST.FocusedNode]:=True;
+
  VST.Expanded[VST.FocusedNode] := True;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 +
25. On the Form Designer double click the Delete captioned button. Paste the following:
  
25. On the Form Designer double click the Delete captioned button. Paste the followings:
+
<syntaxhighlight lang=pascal>
<PRE>
 
 
procedure TForm1.Button3Click(Sender: TObject);
 
procedure TForm1.Button3Click(Sender: TObject);
 
begin
 
begin
 
   VST.DeleteSelectedNodes;
 
   VST.DeleteSelectedNodes;
end;
+
end;
</PRE>
+
</syntaxhighlight>
  
 
26. Run the project by pressing F9 to check. Add some node, child and delete them.
 
26. Run the project by pressing F9 to check. Add some node, child and delete them.
  
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 bellow "If Cell Editing Can't Be Seen".
+
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".
  
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 followings:
+
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:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
   Column: TColumnIndex; NewText: WideString);
 
   Column: TColumnIndex; NewText: WideString);
Var
+
var
Data: PTreeData;
+
  Data: PTreeData;
 
begin
 
begin
Data := VST.GetNodeData(Node);
+
  Data := VST.GetNodeData(Node);
Case Column of
+
  case Column of
  0: Data^.Column0:= NewText;
+
    0: Data^.Column0 := NewText;
  1: Data^.Column1:= NewText;
+
    1: Data^.Column1 := NewText;
  2: Data^.Column2:= NewText;
+
    2: Data^.Column2 := NewText;
End;
+
  end;
end;  
+
end;
</PRE>
+
</syntaxhighlight>
  
So far, example for basic use ends here. You may drop few more buttons on the form to test few more commands given bellow. Next would be, showing checkbox, image, font colour and adding combobox on node.  
+
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.  
  
 +
*Another Way to Add Root Node
  
*Another Way To Add Root Node
+
<syntaxhighlight lang=pascal>
<PRE>
 
 
procedure TForm1.Button8Click(Sender: TObject);
 
procedure TForm1.Button8Click(Sender: TObject);
 
begin
 
begin
 
   with VST do
 
   with VST do
  RootNodeCount:=RootNodeCount+1;
+
    RootNodeCount := RootNodeCount + 1;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
*Another Way To Add Child
+
 
<PRE>
+
*Another Way to Add Child
 +
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button9Click(Sender: TObject);
 
procedure TForm1.Button9Click(Sender: TObject);
 
begin
 
begin
   if Assigned(VST.FocusedNode) Then
+
   if Assigned(VST.FocusedNode) then
  VST.ChildCount[VST.FocusedNode]:=VST.ChildCount[VST.FocusedNode]+1;
+
    VST.ChildCount[VST.FocusedNode] := VST.ChildCount[VST.FocusedNode] + 1;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
*Determine & Delete Children Of A Node
+
 
<PRE>
+
*Determine & Delete Children of A Node
 +
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button4Click(Sender: TObject);
 
procedure TForm1.Button4Click(Sender: TObject);
Var
+
var
c: Integer;
+
  c: Integer;
 
begin
 
begin
  if not Assigned(VST.FocusedNode) then
+
  if not Assigned(VST.FocusedNode) then
 
     Exit;
 
     Exit;
  If VST.HasChildren[VST.FocusedNode] then
+
  if VST.HasChildren[VST.FocusedNode] then
  Begin
+
  begin
  c := VST.ChildCount[VST.FocusedNode];
+
    c := VST.ChildCount[VST.FocusedNode];
  VST.DeleteChildren(VST.FocusedNode);
+
    VST.DeleteChildren(VST.FocusedNode);
  ShowMessage('Number of deleted child:' + #13#10 + IntToStr(c));
+
    ShowMessage('Number of deleted child:' + #13#10 + IntToStr(c));
  End;
+
  end;
end;  
+
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
*Delete Node
 
*Delete Node
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button5Click(Sender: TObject);
 
procedure TForm1.Button5Click(Sender: TObject);
 
begin
 
begin
 
{VST.Clear;  //Delete All Nodes}
 
{VST.Clear;  //Delete All Nodes}
  if not Assigned(VST.FocusedNode) then
+
  if not Assigned(VST.FocusedNode) then
 
     Exit;
 
     Exit;
  VST.DeleteNode(VST.FocusedNode);
+
  VST.DeleteNode(VST.FocusedNode);
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
*Search And Select Node
+
 
<PRE>
+
*Search and Select Node
 +
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button6Click(Sender: TObject);
 
procedure TForm1.Button6Click(Sender: TObject);
Var
+
var
XNode: PVirtualNode;
+
  XNode: PVirtualNode;
Data: PTreeData;
+
  Data: PTreeData;
 
begin
 
begin
XNode:= VST.GetFirst;
+
  XNode := VST.GetFirst;
 +
 
 +
  while XNode <> nil do
 +
  begin
 +
    Data := VST.GetNodeData(XNode);
 +
    if Data^.Column0 = '1' then
 +
    begin
 +
      VST.ClearSelection;
 +
      VST.Selected[XNode] := True;
 +
      VST.SetFocus;
 +
      break;
 +
    end else
 +
    XNode := VST.GetNextSibling(XNode);
 +
  end;
 +
end;
 +
</syntaxhighlight>
  
while XNode <> nil do
 
begin
 
  Data:=VST.GetNodeData(XNode);
 
  if Data^.Column0  = '1' then
 
    Begin
 
    VST.ClearSelection;
 
    VST.Selected[XNode]:=True;
 
    VST.SetFocus;
 
    break;
 
    End
 
    Else
 
    XNode:= VST.GetNextSibling(XNode);
 
End;
 
End;
 
</PRE>
 
 
*Determine Parent
 
*Determine Parent
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button13Click(Sender: TObject);
 
procedure TForm1.Button13Click(Sender: TObject);
 
var
 
var
Line 259: Line 281:
 
   if not Assigned(VST.FocusedNode) then
 
   if not Assigned(VST.FocusedNode) then
 
     Exit;
 
     Exit;
   XNode:=VST.FocusedNode;
+
   XNode := VST.FocusedNode;
 
   while VST.GetNodeLevel(XNode) > 0 do
 
   while VST.GetNodeLevel(XNode) > 0 do
    Begin
+
  begin
 
     XNode := XNode.Parent;
 
     XNode := XNode.Parent;
     VST.Selected[XNode]:= True;
+
     VST.Selected[XNode] := True;
    End;
+
  end;
  VST.Refresh;
+
  VST.Refresh;
  VST.SetFocus;
+
  VST.SetFocus;
end;  
+
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
*Search All
 
*Search All
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button7Click(Sender: TObject);
 
procedure TForm1.Button7Click(Sender: TObject);
Var
+
var
XNode: PVirtualNode;
+
  XNode: PVirtualNode;
Data: PTreeData;
+
  Data: PTreeData;
 +
begin
 +
  if VST.GetFirst = nil then Exit;
 +
  XNode := nil;
 +
  repeat
 +
    if XNode = nil then
 +
      XNode := VST.GetFirst
 +
    else
 +
      XNode := VST.GetNext(XNode);
 +
    Data := VST.GetNodeData(XNode);
 +
    if (Data^.Column0 = '1') or (Data^.Column1 = '1') or (Data^.Column2 = '1') then
 +
    begin
 +
      ShowMessage('Found at Node Level : ' + IntToStr(VST.GetNodeLevel(XNode)));
 +
      break;
 +
    end;
 +
  until XNode = VST.GetLast();
 +
end;
 +
</syntaxhighlight>
 +
 
 +
*Search next
 +
 
 +
<syntaxhighlight lang=pascal>
 +
procedure TForm1.Button8Click(Sender: TObject);
 +
var
 +
  XNode: PVirtualNode;
 +
  Data: PTreeData;
 
begin
 
begin
If VST.GetFirst = nil then Exit;
+
  if not Assigned(VST.GetFirst) then
XNode:=nil;
+
    Exit
Repeat
+
  else
if XNode = nil then XNode:=VST.GetFirst Else XNode:=VST.GetNext(XNode);
+
    XNode := VST.GetFirst;
  Data:=VST.GetNodeData(XNode);
+
  repeat
  If (Data^.Column0 = '1') OR (Data^.Column1 = '1') OR (Data^.Column2 = '1') then
+
    XNode := VST.GetNext(XNode);
    Begin
+
    Data := VST.GetNodeData(XNode);
    ShowMessage('Found at Node Level : ' + IntToStr(VST.GetNodeLevel(XNode)) );
+
    if Pos(LowerCase(SearchEdit.Text), LowerCase(Data^.Column0)) > 0 then
    break;
+
    begin
     End;
+
      VST.FocusedNode := XNode;
   Until XNode = VST.GetLast();
+
      VST.Selected[XNode] := True;
 +
      if MessageDlg('Item found?', mtConfirmation, mbYesNo, 0) = mrYes then
 +
      begin
 +
        VST.Expanded[XNode] := True;
 +
        VST.Refresh;
 +
        VST.SetFocus;
 +
        Break;
 +
      end;
 +
     end;
 +
   until XNode = VST.GetLast;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
*Insert Node
 
*Insert Node
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button12Click(Sender: TObject);
 
procedure TForm1.Button12Click(Sender: TObject);
 
var
 
var
XNode: PVirtualNode;
+
  XNode: PVirtualNode;
 
begin
 
begin
   If Assigned(VST.FocusedNode) then
+
   if Assigned(VST.FocusedNode) then
 
   begin
 
   begin
      XNode := VST.InsertNode(VST.FocusedNode,amInsertBefore);
+
    XNode := VST.InsertNode(VST.FocusedNode,amInsertBefore);
      // To Insert After Selected Node.
+
    // To Insert After Selected Node.
      {XNode := VST.InsertNode(VST.FocusedNode,amInsertAfter);}
+
    {XNode := VST.InsertNode(VST.FocusedNode,amInsertAfter);}
      VST.Refresh;
+
    VST.Refresh;
 
   end;
 
   end;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
*Set Node Height
 
*Set Node Height
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button14Click(Sender: TObject);
 
procedure TForm1.Button14Click(Sender: TObject);
 
begin
 
begin
  If Assigned(VST.FocusedNode) then
+
  if Assigned(VST.FocusedNode) then
  VST.NodeHeight[VST.FocusedNode] := 32;
+
    VST.NodeHeight[VST.FocusedNode] := 32;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
*Save And Load
 
*Save And Load
 +
 
Simple tree (without column) can be saved and loaded as:
 
Simple tree (without column) can be saved and loaded as:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
VST.SaveToFile('filename.dat');
 
VST.SaveToFile('filename.dat');
 
VST.LoadFromFile('filename.dat');
 
VST.LoadFromFile('filename.dat');
</PRE>
+
</syntaxhighlight>
<br>
+
 
 
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:
 
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:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTLoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTLoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
   Stream: TStream);
 
   Stream: TStream);
Var
+
var
Data: PTreeData;
+
  Data: PTreeData;
Len: Integer;
+
  Len: Integer;
 
begin
 
begin
Data := VST.GetNodeData(Node);
+
  Data := VST.GetNodeData(Node);
Stream.read(Len, SizeOf(Len));
+
  Stream.read(Len, SizeOf(Len));
SetLength(Data^.Column0, Len);
+
  SetLength(Data^.Column0, Len);
Stream.read(PChar(Data^.Column0)^, Len);
+
  Stream.read(PChar(Data^.Column0)^, Len);
  
Stream.read(Len, SizeOf(Len));
+
  Stream.read(Len, SizeOf(Len));
SetLength(Data^.Column1, Len);
+
  SetLength(Data^.Column1, Len);
Stream.read(PChar(Data^.Column1)^, Len);
+
  Stream.read(PChar(Data^.Column1)^, Len);
  
Stream.read(Len, SizeOf(Len));
+
  Stream.read(Len, SizeOf(Len));
SetLength(Data^.Column2, Len);
+
  SetLength(Data^.Column2, Len);
Stream.read(PChar(Data^.Column2)^, Len);
+
  Stream.read(PChar(Data^.Column2)^, Len);
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
  
 
Again on Object Inspector's Events tab - scroll down to OnSaveNode, double click and then paste:
 
Again on Object Inspector's Events tab - scroll down to OnSaveNode, double click and then paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTSaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTSaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
   Stream: TStream);
 
   Stream: TStream);
Var
+
var
Data: PTreeData;
+
  Data: PTreeData;
Len: Integer;
+
  Len: Integer;
 
begin
 
begin
Data := VST.GetNodeData(Node);
+
  Data := VST.GetNodeData(Node);
Len := Length(Data^.Column0);
+
  Len := Length(Data^.Column0);
Stream.write(Len, SizeOf(Len));
+
  Stream.write(Len, SizeOf(Len));
Stream.write(PChar(Data^.Column0)^, Len);
+
  Stream.write(PChar(Data^.Column0)^, Len);
  
Len := Length(Data^.Column1);
+
  Len := Length(Data^.Column1);
Stream.write(Len, SizeOf(Len));
+
  Stream.write(Len, SizeOf(Len));
Stream.write(PChar(Data^.Column1)^, Len);
+
  Stream.write(PChar(Data^.Column1)^, Len);
  
Len := Length(Data^.Column2);
+
  Len := Length(Data^.Column2);
Stream.write(Len, SizeOf(Len));
+
  Stream.write(Len, SizeOf(Len));
Stream.write(PChar(Data^.Column2)^, Len);
+
  Stream.write(PChar(Data^.Column2)^, Len);
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
  
 
On Form Designer double click Save captioned button, paste:
 
On Form Designer double click Save captioned button, paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button10Click(Sender: TObject);
 
procedure TForm1.Button10Click(Sender: TObject);
 
begin
 
begin
VST.SaveToFile('C:\vst.dat');
+
  VST.SaveToFile('C:\vst.dat');
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
  
 
On Form Designer double click Load captioned button, paste:
 
On Form Designer double click Load captioned button, paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button11Click(Sender: TObject);
 
procedure TForm1.Button11Click(Sender: TObject);
 
begin
 
begin
 
   VST.LoadFromFile('C:\vst.dat');
 
   VST.LoadFromFile('C:\vst.dat');
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
Now test to save and load tree.
 
Now test to save and load tree.
  
 
*Scroll Problem
 
*Scroll Problem
 +
 
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.
 
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.
  
 
*Column Resize
 
*Column Resize
 +
 
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<br>
 
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<br>
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:<br>
+
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:
  
 
1. Add a variable in the source editor named as CurCol: Integer; So it looks like:
 
1. Add a variable in the source editor named as CurCol: Integer; So it looks like:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
var
 
var
 
   Form1: TForm1;  
 
   Form1: TForm1;  
Line 398: Line 471:
  
 
{ TForm1 }  
 
{ TForm1 }  
</PRE>
+
</syntaxhighlight>
  
 
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:
 
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:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.FormCreate(Sender: TObject);
 
procedure TForm1.FormCreate(Sender: TObject);
 
begin
 
begin
    Form1.OnMouseWheelUp:=@Form1MouseWheelUp;
+
  Form1.OnMouseWheelUp   := @Form1MouseWheelUp;
    Form1.OnMouseWheelDown:=@Form1MouseWheelDown;
+
  Form1.OnMouseWheelDown := @Form1MouseWheelDown;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
  
 
3. Fill the TForm1.Form1MouseWheelUp procedure as:
 
3. Fill the TForm1.Form1MouseWheelUp procedure as:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Form1MouseWheelUp(Sender: TObject; Shift: TShiftState;
 
procedure TForm1.Form1MouseWheelUp(Sender: TObject; Shift: TShiftState;
 
   MousePos: TPoint; var Handled: Boolean);
 
   MousePos: TPoint; var Handled: Boolean);
 
begin
 
begin
If VST.Focused then
+
  if VST.Focused and (ssRight in Shift) then
  if ssRight in Shift then
+
    VST.Header.Columns[CurCol].Width := VST.Header.Columns[CurCol].Width + 10;
VST.Header.Columns[CurCol].Width:= VST.Header.Columns[CurCol].Width + 10;
+
end;
end;  
+
</syntaxhighlight>
</PRE>
 
  
 
4. Fill the TForm1.Form1MouseWheelDown procedure as:
 
4. Fill the TForm1.Form1MouseWheelDown procedure as:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Form1MouseWheelDown(Sender: TObject; Shift: TShiftState;
 
procedure TForm1.Form1MouseWheelDown(Sender: TObject; Shift: TShiftState;
 
   MousePos: TPoint; var Handled: Boolean);
 
   MousePos: TPoint; var Handled: Boolean);
 
begin
 
begin
   If VST.Focused then
+
   if VST.Focused and (ssRight in Shift) then
  if ssRight in Shift then
+
    VST.Header.Columns[CurCol].Width := VST.Header.Columns[CurCol].Width - 10;
VST.Header.Columns[CurCol].Width:= VST.Header.Columns[CurCol].Width - 10;
+
end;
end;  
+
</syntaxhighlight>
</PRE>
 
  
 
5. Go to the Form Designer (press F12), select VST, on Object Inspector's Events tab scroll to OnFocusChanged, double click & paste:
 
5. Go to the Form Designer (press F12), select VST, on Object Inspector's Events tab scroll to OnFocusChanged, double click & paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree;
 
procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree;
 
   Node: PVirtualNode; Column: TColumnIndex);
 
   Node: PVirtualNode; Column: TColumnIndex);
 
begin
 
begin
   CurCol:=Column;
+
   CurCol := Column;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
  
 
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 + "+".
 
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 + "+".
----
 
----
 
  
 
=Checkbox=
 
=Checkbox=
----
+
 
On Form Designer select VST. Go to:<br>
+
On Form Designer select VST. Go to:
 +
 
 
#Object Inspector -> Properties -> CheckImageKind and select ckDarkCheck.
 
#Object Inspector -> Properties -> CheckImageKind and select ckDarkCheck.
#Object Inspector -> Properties -> TreeOptions -> MiscOptions -> toCheckSupport and set it to True.<br>
+
#Object Inspector -> Properties -> TreeOptions -> MiscOptions -> toCheckSupport and set it to True.
Now switch to Events tab.<br>
+
 
*Scroll to OnInitNode. Double click and paste the followings:
+
Now switch to Events tab.
<PRE>
+
 
 +
*Scroll to OnInitNode. Double click and paste the following:
 +
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTInitNode(Sender: TBaseVirtualTree; ParentNode,
 
procedure TForm1.VSTInitNode(Sender: TBaseVirtualTree; ParentNode,
 
   Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
 
   Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
Var
+
var
Level: Integer;
+
  Level: Integer;
 
begin
 
begin
    Level := VST.GetNodeLevel(Node);
+
  Level := VST.GetNodeLevel(Node);
    if Level = 0 then
+
  if Level = 0 then
     Node.CheckType:=ctCheckBox;
+
     Node.CheckType := ctCheckBox;
  
    if Level = 2 then
+
  if Level = 2 then
     Node.CheckType:=ctRadioButton;
+
     Node.CheckType := ctRadioButton;
  
    if Level = 1 then
+
  if Level = 1 then
    begin
+
  begin
     Node.CheckType:=ctTriStateCheckBox;
+
     Node.CheckType := ctTriStateCheckBox;
 
     Node.CheckState := csCheckedNormal;
 
     Node.CheckState := csCheckedNormal;
    end;
+
  end;
  
    if Level = 3 then
+
  if Level = 3 then
     Node.CheckType:=ctButton;
+
     Node.CheckType := ctButton;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
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.
 
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.
 +
 
*Scroll to OnChecked, double click and paste:
 
*Scroll to OnChecked, double click and paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
begin
 
begin
  vst.Refresh;
+
  VST.Refresh;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
*Scroll to OnChecking, double click and paste:
 
*Scroll to OnChecking, double click and paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTChecking(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTChecking(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
   var NewState: TCheckState; var Allowed: Boolean);
 
   var NewState: TCheckState; var Allowed: Boolean);
 
begin
 
begin
VST.Refresh;
+
  VST.Refresh;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
Hope now it is ok. To determine checkbox state use like:<BR>
+
 
 +
Hope now it is ok. To determine checkbox state use like:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
if XNode.CheckState = csCheckedNormal then
 +
ShowMessage('Checked.');
 +
</syntaxhighlight>
  
If XNode.CheckState = csCheckedNormal then<br>
+
Other states are:
ShowMessage('Checked.');<br>
 
  
Other states are:<br>
+
<syntaxhighlight lang=pascal>
<PRE>
 
 
csUncheckedNormal = unchecked and not pressed
 
csUncheckedNormal = unchecked and not pressed
 
csUncheckedPressed = unchecked and pressed
 
csUncheckedPressed = unchecked and pressed
Line 503: Line 589:
 
csMixedNormal = 3-state check box and not pressed
 
csMixedNormal = 3-state check box and not pressed
 
csMixedPressed = 3-state check box and pressed
 
csMixedPressed = 3-state check box and pressed
</PRE>
+
</syntaxhighlight>
 +
 
 +
----
 +
[[user:zoltanleo|Note:]]visually, the <code>CheckState</code> property looks something like this: <br>
 +
[[File:checkstate enum.png]]<br>
 +
----
 +
 
 +
Other types are:
  
Other types are:<br>
+
<syntaxhighlight lang=pascal>
<PRE>
 
 
ctNone
 
ctNone
 
ctTriStateCheckBox
 
ctTriStateCheckBox
 
ctCheckBox
 
ctCheckBox
 
ctRadioButton
 
ctRadioButton
ctButton
+
ctButton</syntaxhighlight>
</PRE>
+
 
 +
----
 +
[[user:zoltanleo|Note:]] visually, the <code>CheckType</code>  property looks something like this:<br>
 +
[[File:checktype enum.png]]<br>
 +
----
 +
 
 
*To Catch Checkbox's Button (ctButton) Click
 
*To Catch Checkbox's Button (ctButton) Click
 
Go to Object Inspector's Events tab. Scroll to OnChecked, double click and paste:
 
Go to Object Inspector's Events tab. Scroll to OnChecked, double click and paste:
<PRE>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
 
begin
 
begin
if Node.CheckType = ctButton then
+
  if Node.CheckType = ctButton then
ShowMessage('Ok.');
+
    ShowMessage('Ok.');
VST.Refresh;
+
  VST.Refresh;
end;
+
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
End of checkbox.<br>
 
End of checkbox.<br>
  
----
+
=Images=
----
+
 
=Image & Font Colour=
 
----
 
 
To show image on VST nodes, a list of image should be created.
 
To show image on VST nodes, a list of image should be created.
 +
 
*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/  
 
*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/  
 +
 
*Now on the Form Designer select VST, and on Object Inspector's Properties tab, scroll to Images and select ImageList1
 
*Now on the Form Designer select VST, and on Object Inspector's Properties tab, scroll to Images and select ImageList1
 +
 
*On Object Inspector's Events tab scroll to OnGetImageIndex, double click and paste:
 
*On Object Inspector's Events tab scroll to OnGetImageIndex, double click and paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
   Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean;
 
   Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean;
 
   var ImageIndex: Integer);
 
   var ImageIndex: Integer);
 
begin
 
begin
if Kind in [ikNormal , ikSelected] then // Either Selected or not
+
  if Kind in [ikNormal , ikSelected] then // Either Selected or not
begin
+
  begin
  if Column = 0 then                    // if 1st Column
+
    if Column = 0 then                    // if 1st Column
    ImageIndex:=0;                     // 1st Image of the ImageList1
+
      ImageIndex := 0;                   // 1st Image of the ImageList1
  
  if Column = 1 then                    // if 2nd Column
+
    if Column = 1 then                    // if 2nd Column
    ImageIndex:=1;                     // 2nd Image of the ImageList1
+
      ImageIndex := 1;                   // 2nd Image of the ImageList1
  
  if Sender.FocusedNode = Node then    // Only show if Focused
+
    if Sender.FocusedNode = Node then    // Only show if Focused
  if Column =2 then                   // if 3rd Column
+
      if Column = 2 then                 // if 3rd Column
    ImageIndex:=2;                     // 3rd Image of the ImageList1
+
        ImageIndex := 2;                 // 3rd Image of the ImageList1
 
   end;
 
   end;
{Sender.NodeHeight[node]:=40; //If Image is big}
+
  {Sender.NodeHeight[node] := 40; //If Image is big}
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
=====Font Colour=====
+
 
 +
=Font Colour=
 +
 
 
On the Form Designer select VST, and on Object Inspector's Events tab, scroll to OnPaintText, double click and paste:
 
On the Form Designer select VST, and on Object Inspector's Events tab, scroll to OnPaintText, double click and paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTPaintText(Sender: TBaseVirtualTree;
 
procedure TForm1.VSTPaintText(Sender: TBaseVirtualTree;
 
   const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
 
   const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
 
   TextType: TVSTTextType);
 
   TextType: TVSTTextType);
Var
+
var
Data: PTreeData;
+
  Data: PTreeData;
 
begin
 
begin
Data := VST.GetNodeData(Node);
+
  Data := VST.GetNodeData(Node);
 
   
 
   
if Data^.Column0 = 'sky' then
+
  if Data^.Column0 = 'sky' then
  TargetCanvas.Font.Color:=clBlue;
+
    TargetCanvas.Font.Color := clBlue;
 
    
 
    
if Column = 1 then
+
  if Column = 1 then
begin
+
  begin
  TargetCanvas.Font.Color:=clRed;
+
    TargetCanvas.Font.Color := clRed;
  TargetCanvas.Font.Style:= Font.Style + [fsItalic];
+
    TargetCanvas.Font.Style := Font.Style + [fsItalic];
end;
+
  end;
 
   
 
   
if Column = 2 then
+
  if Column = 2 then
begin
+
  begin
//  ImageList1.Draw(Form1.Canvas,-1,-1,2); {draw top left of form, 3rd image of ImageList1??}
+
//  ImageList1.Draw(Form1.Canvas, -1, -1, 2); {draw top left of form, 3rd image of ImageList1??}
  TargetCanvas.Font.Size:= 9;
+
    TargetCanvas.Font.Size := 9;
  TargetCanvas.Font.Color:=clHighlightText;
+
    TargetCanvas.Font.Color := clHighlightText;
end;
+
  end;
end;  
+
end;
</PRE>
+
</syntaxhighlight>
Image & Font Colour ends here.
 
  
 
=Adding A Combobox=
 
=Adding A Combobox=
 +
 
*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.
 
*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.
  
*Bellow 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:
+
*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:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
uses
 
uses
 
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
 
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
 
   VirtualStringTree, VirtualTrees, combo;
 
   VirtualStringTree, VirtualTrees, combo;
</PRE>
+
</syntaxhighlight>
  
 
*The combo.pas unit
 
*The combo.pas unit
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
unit combo;
 
unit combo;
  
Line 602: Line 707:
  
 
uses
 
uses
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
+
   Classes, SysUtils, LResources, LMessage, LCLType, Forms, Controls, Graphics, Dialogs,
   VirtualStringTree, VirtualTrees, messages, windows, StdCtrls;
+
   VirtualStringTree, VirtualTrees, StdCtrls, windows;
  
 
type
 
type
Line 621: Line 726:
 
     function GetBounds: TRect; stdcall;
 
     function GetBounds: TRect; stdcall;
 
     function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
 
     function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
     procedure ProcessMessage(var Message: TMessage); stdcall;
+
     procedure ProcessMessage(var Message: TLMessage); stdcall;
 
     procedure SetBounds(R: TRect); stdcall;
 
     procedure SetBounds(R: TRect); stdcall;
 
   end;
 
   end;
Line 635: Line 740:
 
procedure TStringEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
 
procedure TStringEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
 
begin
 
begin
    case Key of
+
  case Key of
 
     VK_ESCAPE:
 
     VK_ESCAPE:
 
       begin
 
       begin
Line 642: Line 747:
 
         FTree.setfocus;
 
         FTree.setfocus;
 
       end;
 
       end;
      VK_RETURN:
+
    VK_RETURN:
 
       begin
 
       begin
 
       PostMessage(FTree.Handle, WM_KEYDOWN, VK_DOWN, 0);
 
       PostMessage(FTree.Handle, WM_KEYDOWN, VK_DOWN, 0);
Line 649: Line 754:
 
       FTree.setfocus;
 
       FTree.setfocus;
 
       end;
 
       end;
    End; //case
+
  end; //case
 
end;
 
end;
 
  
 
function TStringEditLink.BeginEdit: Boolean;
 
function TStringEditLink.BeginEdit: Boolean;
 
begin
 
begin
 
   Result := True;
 
   Result := True;
   //FEdit.Height:=(FTree.DefaultNodeHeight - 1); //Needed for editbox. Not combo
+
   //FEdit.Height := (FTree.DefaultNodeHeight - 1); //Needed for editbox. Not combo
 
   FEdit.Show;
 
   FEdit.Show;
   TComboBox(FEdit).DroppedDown:=True;
+
   TComboBox(FEdit).DroppedDown := True;
 
   FEdit.SetFocus;
 
   FEdit.SetFocus;
 
end;
 
end;
Line 667: Line 771:
 
   FEdit.Hide;
 
   FEdit.Hide;
 
end;
 
end;
 
  
 
function TStringEditLink.EndEdit: Boolean;
 
function TStringEditLink.EndEdit: Boolean;
 
var
 
var
   S: WideString;
+
   s: String;
 
begin
 
begin
 
   Result := True;
 
   Result := True;
   S:= TComboBox(FEdit).Text;
+
   s := TComboBox(FEdit).Text;
   FTree.Text[FNode, FColumn] := S;
+
   FTree.Text[FNode, FColumn] := s;
  
 
   FTree.InvalidateNode(FNode);
 
   FTree.InvalidateNode(FNode);
Line 681: Line 784:
 
   FTree.SetFocus;
 
   FTree.SetFocus;
 
end;
 
end;
 
  
 
function TStringEditLink.GetBounds: TRect;
 
function TStringEditLink.GetBounds: TRect;
Line 687: Line 789:
 
   Result := FEdit.BoundsRect;
 
   Result := FEdit.BoundsRect;
 
end;
 
end;
 
  
 
function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
 
function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
Line 700: Line 801:
  
 
   FEdit := TComboBox.Create(nil);
 
   FEdit := TComboBox.Create(nil);
  with FEdit as TComboBox do
+
  with FEdit as TComboBox do
    begin
+
    begin
          Visible := False;
+
      Visible := False;
          Parent := Tree;
+
      Parent := Tree;
          Items.Add('Google');
+
      Items.Add('Google');
          Items.Add('Yahoo');
+
      Items.Add('Yahoo');
          Items.Add('Altavista');
+
      Items.Add('Altavista');
          OnKeyDown := EditKeyDown;
+
      OnKeyDown := EditKeyDown;
      end;
+
    end;
 
end;
 
end;
  
 
+
procedure TStringEditLink.ProcessMessage(var Message: TLMessage);
procedure TStringEditLink.ProcessMessage(var Message: TMessage);
 
 
begin
 
begin
 
   FEdit.WindowProc(Message);
 
   FEdit.WindowProc(Message);
 
end;
 
end;
 
  
 
procedure TStringEditLink.SetBounds(R: TRect);
 
procedure TStringEditLink.SetBounds(R: TRect);
 
var
 
var
 
   Dummy: Integer;
 
   Dummy: Integer;
 
 
begin
 
begin
 
   FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
 
   FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
Line 727: Line 825:
 
end;
 
end;
  
End.
+
end.
</PRE>
+
</syntaxhighlight>
  
 
*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.
 
*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.
  
 
*Switch to Object Inspector's Events tab. Scroll to OnCreateEditor, double click and paste:
 
*Switch to Object Inspector's Events tab. Scroll to OnCreateEditor, double click and paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTCreateEditor(Sender: TBaseVirtualTree;
 
procedure TForm1.VSTCreateEditor(Sender: TBaseVirtualTree;
 
   Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
 
   Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
 
begin
 
begin
   EditLink:=TStringEditLink.Create;
+
   EditLink := TStringEditLink.Create;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
  
 
*On Object Inspector's Events tab. Scroll to OnNewText, double click and paste:
 
*On Object Inspector's Events tab. Scroll to OnNewText, double click and paste:
<PRE>
+
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 
   Column: TColumnIndex; NewText: WideString);
 
   Column: TColumnIndex; NewText: WideString);
Var
+
var
Data: PTreeData;
+
  Data: PTreeData;
 
begin
 
begin
Data := VST.GetNodeData(Node);
+
  Data := VST.GetNodeData(Node);
Case Column of
+
  case Column of
0: Data^.Column0:= NewText;
+
    0: Data^.Column0 := NewText;
1: Data^.Column1:= NewText;
+
    1: Data^.Column1 := NewText;
2: Data^.Column2:= NewText;
+
    2: Data^.Column2 := NewText;
End;
+
  end;
 
end;
 
end;
</PRE>
+
</syntaxhighlight>
 +
 
 
Run program, select a node and press F2 to get combobox. On pressing Enter new value should appear on the node.
 
Run program, select a node and press F2 to get combobox. On pressing Enter new value should appear on the node.
  
 
=If Cell Editing Can't Be Seen=
 
=If Cell Editing Can't Be Seen=
----
+
 
 
Open VirtualStringTree.pas unit file (if you are still on the above example project, right click on VirtualStringTree  
 
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;".
 
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:
 
It looks like:
<PRE>  
+
 
 +
<syntaxhighlight lang=pascal>
 
function TStringEditLink.BeginEdit: Boolean; stdcall;
 
function TStringEditLink.BeginEdit: Boolean; stdcall;
  
Line 777: Line 879:
 
     FEdit.SetFocus;
 
     FEdit.SetFocus;
 
   end;
 
   end;
end;  
+
end;
</PRE>     
+
</syntaxhighlight>     
  
 
Now add "FEdit.Height:=18;". It should look like:
 
Now add "FEdit.Height:=18;". It should look like:
  
<PRE>
+
<syntaxhighlight lang=pascal>
 
function TStringEditLink.BeginEdit: Boolean; stdcall;
 
function TStringEditLink.BeginEdit: Boolean; stdcall;
  
Line 795: Line 897:
 
     FEdit.SelectAll;
 
     FEdit.SelectAll;
 
     FEdit.SetFocus;
 
     FEdit.SetFocus;
     FEdit.Height:=18; // <--- Added this line.
+
     FEdit.Height := 18; // <--- Added this line.
 
   end;
 
   end;
end;  
+
end;
</PRE>
+
</syntaxhighlight>
  
 
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.
 
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.
<BR>
 
Thank you.<BR>
 
--[[User:Rabiul|Rabiul]] 21:37, 20 October 2008 (CEST)<BR>
 
Alhamdulillah.
 
  
----
+
=External links=
[http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_Pascal Programando en Pascal] - Spanish tutorial focused on FPC/Lazarus, hosted in Wikibooks.
+
 
 +
* [http://www.soft-gems.net Virtual Treeview tutorials/docs]
 +
* [http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_Pascal Programando en Pascal] - Spanish tutorial focused on FPC/Lazarus, hosted in Wikibooks.

Latest revision as of 10:36, 27 February 2023

English (en) español (es) français (fr) polski (pl) русский (ru)

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.

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.

Basic Tree Listview With 3 Columns

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

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.

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:

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
  VirtualTrees;  // or laz.VirtualTrees in Laz 2.2+

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.

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.

6. Click on Text. Type Column2.

7. Click on Width, type 100 and press enter.

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

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.

10. Scroll down to Style set it to hsFlatButtons.

11. Scroll down to TreeOptions(expand) -> MiscOption(expand), set toEditable to True. Set toGridExtensions to True.

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.

13. Now to add 3 buttons on the form. Get these from the component palette - Standard Tab ("OK" Label).

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.

15. Keep this here and get to the Source Editor (press F12). On Source Editor replace the line:

{$mode objfpc}{$H+} with {$MODE DELPHI}

16. Under the "implementation" paste following lines:

type
  PTreeData = ^TTreeData;
  TTreeData = record
    Column0: String;
    Column1: String;
    Column2: String;
  end;

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:

procedure TForm1.VSTChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
begin
  VST.Refresh;
end;

18. Scroll to onFocusChanged. Double click and paste the following:

procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex);
begin
  VST.Refresh;
end;

19. Scroll to onFreeNode. Double click and paste the following:

procedure TForm1.VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  Data: PTreeData;
begin
  Data := VST.GetNodeData(Node);
  if Assigned(Data) then begin
    Data^.Column0 := '';
    Data^.Column1 := '';
    Data^.Column2 := '';
  end;
end;

20. Scroll down to onGetNodeDataSize. Double click & paste the following:

procedure TForm1.VSTGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
begin
  NodeDataSize := SizeOf(TTreeData);
end;

21. Scroll to onGetText. Double click & paste the following:

procedure TForm1.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
  Data: PTreeData;
begin
  Data := VST.GetNodeData(Node);
  case Column of
    0: CellText := Data^.Column0;
    1: CellText := Data^.Column1;
    2: CellText := Data^.Column2;
  end;
end;

22. Press F12 to get Form Designer. Double click AddRoot button. Paste the following:

procedure TForm1.Button1Click(Sender: TObject);
var
  Data: PTreeData;
  XNode: PVirtualNode;
  Rand: Integer;
begin
  Randomize;
  Rand := Random(99);
  XNode := VST.AddChild(nil);
  
  if VST.AbsoluteIndex(XNode) > -1 then
  begin
    Data := VST.GetNodeData(Xnode);
    Data^.Column0 := 'One ' + IntToStr(Rand);
    Data^.Column1 := 'Two ' + IntToStr(Rand + 10);
    Data^.Column2 := 'Three ' + IntToStr(Rand - 10);
  end; 
end;

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.

24. Stop execution. On the Form Designer double click the AddChild captioned button. Paste the following:

procedure TForm1.Button2Click(Sender: TObject);
var
  XNode: PVirtualNode;
  Data: PTreeData;
begin
  if not Assigned(VST.FocusedNode) then
    Exit;
    
  XNode := VST.AddChild(VST.FocusedNode);
  Data := VST.GetNodeData(Xnode);
    
  Data^.Column0 := 'Ch 1';
  Data^.Column1 := 'Ch 2';
  Data^.Column2 := 'Ch 3';

  VST.Expanded[VST.FocusedNode] := True;
end;

25. On the Form Designer double click the Delete captioned button. Paste the following:

procedure TForm1.Button3Click(Sender: TObject);
begin
  VST.DeleteSelectedNodes;
end;

26. Run the project by pressing F9 to check. Add some node, child and delete them.

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

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:

procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; NewText: WideString);
var
  Data: PTreeData;
begin
  Data := VST.GetNodeData(Node);
  case Column of
    0: Data^.Column0 := NewText;
    1: Data^.Column1 := NewText;
    2: Data^.Column2 := NewText;
  end;
end;

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.

  • Another Way to Add Root Node
procedure TForm1.Button8Click(Sender: TObject);
begin
  with VST do
    RootNodeCount := RootNodeCount + 1;
end;
  • Another Way to Add Child
procedure TForm1.Button9Click(Sender: TObject);
begin
  if Assigned(VST.FocusedNode) then
    VST.ChildCount[VST.FocusedNode] := VST.ChildCount[VST.FocusedNode] + 1;
end;
  • Determine & Delete Children of A Node
procedure TForm1.Button4Click(Sender: TObject);
var
  c: Integer;
begin
  if not Assigned(VST.FocusedNode) then
    Exit;
  if VST.HasChildren[VST.FocusedNode] then
  begin
    c := VST.ChildCount[VST.FocusedNode];
    VST.DeleteChildren(VST.FocusedNode);
    ShowMessage('Number of deleted child:' + #13#10 + IntToStr(c));
  end;
end;
  • Delete Node
procedure TForm1.Button5Click(Sender: TObject);
begin
{VST.Clear;  //Delete All Nodes}
  if not Assigned(VST.FocusedNode) then
    Exit;
  VST.DeleteNode(VST.FocusedNode);
end;
  • Search and Select Node
procedure TForm1.Button6Click(Sender: TObject);
var
  XNode: PVirtualNode;
  Data: PTreeData;
begin
  XNode := VST.GetFirst;

  while XNode <> nil do
  begin
    Data := VST.GetNodeData(XNode);
    if Data^.Column0 = '1' then
    begin
      VST.ClearSelection;
      VST.Selected[XNode] := True;
      VST.SetFocus;
      break;
    end else
    XNode := VST.GetNextSibling(XNode);
  end;
end;
  • Determine Parent
procedure TForm1.Button13Click(Sender: TObject);
var
  XNode: PVirtualNode;
begin
  if not Assigned(VST.FocusedNode) then
    Exit;
  XNode := VST.FocusedNode;
  while VST.GetNodeLevel(XNode) > 0 do
  begin
    XNode := XNode.Parent;
    VST.Selected[XNode] := True;
  end;
  VST.Refresh;
  VST.SetFocus;
end;
  • Search All
procedure TForm1.Button7Click(Sender: TObject);
var
  XNode: PVirtualNode;
  Data: PTreeData;
begin
  if VST.GetFirst = nil then Exit;
  XNode := nil;
  repeat
    if XNode = nil then 
      XNode := VST.GetFirst 
    else 
      XNode := VST.GetNext(XNode);
    Data := VST.GetNodeData(XNode);
    if (Data^.Column0 = '1') or (Data^.Column1 = '1') or (Data^.Column2 = '1') then
    begin
      ShowMessage('Found at Node Level : ' + IntToStr(VST.GetNodeLevel(XNode)));
      break;
    end;
  until XNode = VST.GetLast();
end;
  • Search next
procedure TForm1.Button8Click(Sender: TObject);
var
  XNode: PVirtualNode;
  Data: PTreeData;
begin
  if not Assigned(VST.GetFirst) then
    Exit
  else
    XNode := VST.GetFirst;
  repeat
    XNode := VST.GetNext(XNode);
    Data := VST.GetNodeData(XNode);
    if Pos(LowerCase(SearchEdit.Text), LowerCase(Data^.Column0)) > 0 then
    begin
      VST.FocusedNode := XNode;
      VST.Selected[XNode] := True;
      if MessageDlg('Item found?', mtConfirmation, mbYesNo, 0) = mrYes then
      begin
        VST.Expanded[XNode] := True;
        VST.Refresh;
        VST.SetFocus;
        Break;
      end;
    end;
  until XNode = VST.GetLast;
end;
  • Insert Node
procedure TForm1.Button12Click(Sender: TObject);
var
  XNode: PVirtualNode;
begin
  if Assigned(VST.FocusedNode) then
  begin
    XNode := VST.InsertNode(VST.FocusedNode,amInsertBefore);
    // To Insert After Selected Node.
    {XNode := VST.InsertNode(VST.FocusedNode,amInsertAfter);}
    VST.Refresh;
  end;
end;
  • Set Node Height
procedure TForm1.Button14Click(Sender: TObject);
begin
  if Assigned(VST.FocusedNode) then
    VST.NodeHeight[VST.FocusedNode] := 32;
end;
  • Save And Load

Simple tree (without column) can be saved and loaded as:

VST.SaveToFile('filename.dat');
VST.LoadFromFile('filename.dat');

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:

procedure TForm1.VSTLoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Stream: TStream);
var
  Data: PTreeData;
  Len: Integer;
begin
  Data := VST.GetNodeData(Node);
  Stream.read(Len, SizeOf(Len));
  SetLength(Data^.Column0, Len);
  Stream.read(PChar(Data^.Column0)^, Len);

  Stream.read(Len, SizeOf(Len));
  SetLength(Data^.Column1, Len);
  Stream.read(PChar(Data^.Column1)^, Len);

  Stream.read(Len, SizeOf(Len));
  SetLength(Data^.Column2, Len);
  Stream.read(PChar(Data^.Column2)^, Len);
end;

Again on Object Inspector's Events tab - scroll down to OnSaveNode, double click and then paste:

procedure TForm1.VSTSaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Stream: TStream);
var
  Data: PTreeData;
  Len: Integer;
begin
  Data := VST.GetNodeData(Node);
  Len := Length(Data^.Column0);
  Stream.write(Len, SizeOf(Len));
  Stream.write(PChar(Data^.Column0)^, Len);

  Len := Length(Data^.Column1);
  Stream.write(Len, SizeOf(Len));
  Stream.write(PChar(Data^.Column1)^, Len);

  Len := Length(Data^.Column2);
  Stream.write(Len, SizeOf(Len));
  Stream.write(PChar(Data^.Column2)^, Len);
end;

On Form Designer double click Save captioned button, paste:

procedure TForm1.Button10Click(Sender: TObject);
begin
  VST.SaveToFile('C:\vst.dat');
end;

On Form Designer double click Load captioned button, paste:

procedure TForm1.Button11Click(Sender: TObject);
begin
  VST.LoadFromFile('C:\vst.dat');
end;

Now test to save and load tree.

  • Scroll Problem

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.

  • Column Resize

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

1. Add a variable in the source editor named as CurCol: Integer; So it looks like:

var
  Form1: TForm1; 
  CurCol: Integer; // <- Add this line only.

implementation

{ TForm1 }

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:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.OnMouseWheelUp   := @Form1MouseWheelUp;
  Form1.OnMouseWheelDown := @Form1MouseWheelDown;
end;

3. Fill the TForm1.Form1MouseWheelUp procedure as:

procedure TForm1.Form1MouseWheelUp(Sender: TObject; Shift: TShiftState;
  MousePos: TPoint; var Handled: Boolean);
begin
  if VST.Focused and (ssRight in Shift) then
    VST.Header.Columns[CurCol].Width := VST.Header.Columns[CurCol].Width + 10;
end;

4. Fill the TForm1.Form1MouseWheelDown procedure as:

procedure TForm1.Form1MouseWheelDown(Sender: TObject; Shift: TShiftState;
  MousePos: TPoint; var Handled: Boolean);
begin
  if VST.Focused and (ssRight in Shift) then
    VST.Header.Columns[CurCol].Width := VST.Header.Columns[CurCol].Width - 10;
end;

5. Go to the Form Designer (press F12), select VST, on Object Inspector's Events tab scroll to OnFocusChanged, double click & paste:

procedure TForm1.VSTFocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
begin
  CurCol := Column;
end;

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

Checkbox

On Form Designer select VST. Go to:

  1. Object Inspector -> Properties -> CheckImageKind and select ckDarkCheck.
  2. Object Inspector -> Properties -> TreeOptions -> MiscOptions -> toCheckSupport and set it to True.

Now switch to Events tab.

  • Scroll to OnInitNode. Double click and paste the following:
procedure TForm1.VSTInitNode(Sender: TBaseVirtualTree; ParentNode,
  Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  Level: Integer;
begin
  Level := VST.GetNodeLevel(Node);
  if Level = 0 then
    Node.CheckType := ctCheckBox;

  if Level = 2 then
    Node.CheckType := ctRadioButton;

  if Level = 1 then
  begin
    Node.CheckType  := ctTriStateCheckBox;
    Node.CheckState := csCheckedNormal;
  end;

  if Level = 3 then
    Node.CheckType := ctButton;
end;

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.

  • Scroll to OnChecked, double click and paste:
procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
begin
  VST.Refresh;
end;
  • Scroll to OnChecking, double click and paste:
procedure TForm1.VSTChecking(Sender: TBaseVirtualTree; Node: PVirtualNode;
  var NewState: TCheckState; var Allowed: Boolean);
begin
  VST.Refresh;
end;

Hope now it is ok. To determine checkbox state use like:

if XNode.CheckState = csCheckedNormal then
ShowMessage('Checked.');

Other states are:

csUncheckedNormal = unchecked and not pressed
csUncheckedPressed = unchecked and pressed
csCheckedNormal = checked and not pressed
csCheckedPressed = checked and pressed
csMixedNormal = 3-state check box and not pressed
csMixedPressed = 3-state check box and pressed

Note:visually, the CheckState property looks something like this:
checkstate enum.png


Other types are:

ctNone
ctTriStateCheckBox
ctCheckBox
ctRadioButton
ctButton

Note: visually, the CheckType property looks something like this:
checktype enum.png


  • To Catch Checkbox's Button (ctButton) Click

Go to Object Inspector's Events tab. Scroll to OnChecked, double click and paste:

procedure TForm1.VSTChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
begin
  if Node.CheckType = ctButton then
    ShowMessage('Ok.');
  VST.Refresh;
end;

End of checkbox.

Images

To show image on VST nodes, a list of image should be created.

  • 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/
  • Now on the Form Designer select VST, and on Object Inspector's Properties tab, scroll to Images and select ImageList1
  • On Object Inspector's Events tab scroll to OnGetImageIndex, double click and paste:
procedure TForm1.VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean;
  var ImageIndex: Integer);
begin
  if Kind in [ikNormal , ikSelected] then // Either Selected or not
  begin
    if Column = 0 then                    // if 1st Column
      ImageIndex := 0;                    // 1st Image of the ImageList1

    if Column = 1 then                    // if 2nd Column
      ImageIndex := 1;                    // 2nd Image of the ImageList1

    if Sender.FocusedNode = Node then     // Only show if Focused
      if Column = 2 then                  // if 3rd Column
        ImageIndex := 2;                  // 3rd Image of the ImageList1
  end;
  {Sender.NodeHeight[node] := 40; //If Image is big}
end;

Font Colour

On the Form Designer select VST, and on Object Inspector's Events tab, scroll to OnPaintText, double click and paste:

procedure TForm1.VSTPaintText(Sender: TBaseVirtualTree;
  const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType);
var
  Data: PTreeData;
begin
  Data := VST.GetNodeData(Node);
 
  if Data^.Column0 = 'sky' then
    TargetCanvas.Font.Color := clBlue;
  
  if Column = 1 then
  begin
    TargetCanvas.Font.Color := clRed;
    TargetCanvas.Font.Style := Font.Style + [fsItalic];
  end;
 
  if Column = 2 then
  begin
//  ImageList1.Draw(Form1.Canvas, -1, -1, 2); {draw top left of form, 3rd image of ImageList1??}
    TargetCanvas.Font.Size  := 9;
    TargetCanvas.Font.Color := clHighlightText;
  end;
end;

Adding A Combobox

  • 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.
  • 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:
uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
  VirtualStringTree, VirtualTrees, combo;
  • The combo.pas unit
unit combo;

{$mode delphi}

interface

uses
  Classes, SysUtils, LResources, LMessage, LCLType, Forms, Controls, Graphics, Dialogs,
  VirtualStringTree, VirtualTrees, StdCtrls, windows;

type
  TStringEditLink = class(TInterfacedObject, IVTEditLink)
  private
    FEdit: TWinControl;
    FTree: TVirtualStringTree;
    FNode: PVirtualNode;
    FColumn: Integer;
  protected
    procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  public
    destructor Destroy; override;
    function BeginEdit: Boolean; stdcall;
    function CancelEdit: Boolean; stdcall;
    function EndEdit: Boolean; stdcall;
    function GetBounds: TRect; stdcall;
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
    procedure ProcessMessage(var Message: TLMessage); stdcall;
    procedure SetBounds(R: TRect); stdcall;
  end;

implementation

destructor TStringEditLink.Destroy;
begin
  FEdit.Free;
  inherited;
end;

procedure TStringEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  case Key of
    VK_ESCAPE:
      begin
        FTree.CancelEditNode;
        Key := 0;
        FTree.setfocus;
      end;
    VK_RETURN:
      begin
       PostMessage(FTree.Handle, WM_KEYDOWN, VK_DOWN, 0);
       Key := 0;
       FTree.EndEditNode;
       FTree.setfocus;
      end;
  end; //case
end;

function TStringEditLink.BeginEdit: Boolean;
begin
  Result := True;
  //FEdit.Height := (FTree.DefaultNodeHeight - 1); //Needed for editbox. Not combo
  FEdit.Show;
  TComboBox(FEdit).DroppedDown := True;
  FEdit.SetFocus;
end;

function TStringEditLink.CancelEdit: Boolean;
begin
  Result := True;
  FEdit.Hide;
end;

function TStringEditLink.EndEdit: Boolean;
var
  s: String;
begin
  Result := True;
  s := TComboBox(FEdit).Text;
  FTree.Text[FNode, FColumn] := s;

  FTree.InvalidateNode(FNode);
  FEdit.Hide;
  FTree.SetFocus;
end;

function TStringEditLink.GetBounds: TRect;
begin
  Result := FEdit.BoundsRect;
end;

function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
begin
  Result := True;
  FTree := Tree as TVirtualStringTree;
  FNode := Node;
  FColumn := Column;

  FEdit.Free;
  FEdit := nil;

  FEdit := TComboBox.Create(nil);
  with FEdit as TComboBox do
    begin
      Visible := False;
      Parent := Tree;
      Items.Add('Google');
      Items.Add('Yahoo');
      Items.Add('Altavista');
      OnKeyDown := EditKeyDown;
    end;
end;

procedure TStringEditLink.ProcessMessage(var Message: TLMessage);
begin
  FEdit.WindowProc(Message);
end;

procedure TStringEditLink.SetBounds(R: TRect);
var
  Dummy: Integer;
begin
  FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
  FEdit.BoundsRect := R;
end;

end.
  • 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.
  • Switch to Object Inspector's Events tab. Scroll to OnCreateEditor, double click and paste:
procedure TForm1.VSTCreateEditor(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
begin
  EditLink := TStringEditLink.Create;
end;
  • On Object Inspector's Events tab. Scroll to OnNewText, double click and paste:
procedure TForm1.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; NewText: WideString);
var
  Data: PTreeData;
begin
  Data := VST.GetNodeData(Node);
  case Column of
    0: Data^.Column0 := NewText;
    1: Data^.Column1 := NewText;
    2: Data^.Column2 := NewText;
  end;
end;

Run program, select a node and press F2 to get combobox. On pressing Enter new value should appear on the node.

If Cell Editing Can't Be Seen

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:

function TStringEditLink.BeginEdit: Boolean; stdcall;

// Notifies the edit link that editing can start now. Descentants may cancel node edit
// by returning False.

begin
  Result := not FStopping;
  if Result then
  begin
    FEdit.Show;
    FEdit.SelectAll;
    FEdit.SetFocus;
  end;
end;

Now add "FEdit.Height:=18;". It should look like:

function TStringEditLink.BeginEdit: Boolean; stdcall;

// Notifies the edit link that editing can start now. Descentants may cancel node edit
// by returning False.

begin
  Result := not FStopping;
  if Result then
  begin
    FEdit.Show;
    FEdit.SelectAll;
    FEdit.SetFocus;
    FEdit.Height := 18; // <--- Added this line.
  end;
end;

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.

External links