Monday 27 July 2020

TTreeview your TOutline

- or migrating a TOutline to a TTreeview many years too late.


HighDPI themed Delphi 10.4 TOutline and TTreeview

The task to migrate from a working old TOutline to a TTreeview was long overdue in a small program I almost use daily - and its code had not really been touched since it was created.

TOutline was included in Delphi 1 back in the Win 3.1 days, and since Windows did not have any Treeview/Outline in its common controls it was purely VCL, not a wrapper.

It has for many years been advised to not use TOutline for any new projects, but instead use TTreeview or similar - but once in a while I do bump into this component - and this time I thought I would write a small and incomplete migration guide.

I will just list the TOutline related code, and the TTreeview euiqvalent - with reference to the sample application shown in the image at the top.

// TOutline code
Outline1.Clear;
var ParentIdx: Integer := Outline1.AddChild(0, 'Root node');  // Returns absolute index of node

for var I: integer := 1 to 5 do
  Outline1.AddChild(ParentIdx, 'ChildNode '+I.ToString);

Outline1.Items[1].Expand;

Outline1.SelectedItem := 4;

// TTreeview code
TreeView1.HideSelection := False; // Show selection even if focus is lost.
Treeview1.Items.Clear;
var ParentNode: TTreeNode := TreeView1.Items.AddChild(nil, 'Root node');

for var I: integer := 1 to 5 do
  TreeView1.Items.AddChild(ParentNode, 'ChildNode '+I.ToString);

TreeView1.Items[0].Expand(True);

TreeView1.Selected := TreeView1.Items[3];
TreeView1.OnClick(nil);

So as can be seen is TOutline more integer index based, compared to TTreeview more relying on its TTreeNodes - which recently had it's Item property set to default, so no need to write Treeview1.Items.Item[3] anymore - Treeview1.Items[3] will do.

Another thing to be aware of is also that TOutline indexes are 1-based, whereas TTreeview is 0-based, and a node's index on a TTreeNode is related to its Parent, if you want the comparable to the TOutline items index, and the absolute index for the full TTreeview you can use TTreeNode.AbsoluteIndex.

Setting TOutline.SelectedItem does fire an OnClick event, so on the TTreeview it is done by hand.

Here are the onClick for the two components shown:

Label1.Caption := 'SelectedItem: '+Outline1.SelectedItem.ToString + sLineBreak + sLineBreak +
    'FullPath: '+Outline1.Items[Outline1.SelectedItem].FullPath;

Label2.Caption := 'Selected AbsoluteIndex: '+ TreeView1.Selected.AbsoluteIndex.ToString +
    ', Index: '+ TreeView1.Selected.Index.ToString + sLineBreak + sLineBreak +
    'FullPath by Class helper: '+TreeView1.Selected.FullPath;

TTreeNode does not have a FullPath property as TOutlineNode does, so I added a small class helper:

TTreeNodeHelper = class helper for TTreeNode
      function FullPath: string;
    end;
....
function TTreeNodeHelper.FullPath: string;
var
  node: TTreeNode;
begin
  node := Self;
  Result := node.Text;
  while node.Parent<>nil do
  begin
    Result := node.Parent.Text + '\' + Result;
    node := node.Parent;
  end;
end;

To get the selected Node:
Node := Outline1.Items[Outline1.SelectedItem];
compared to:
Node := TreeView1.Selected;

Check if anything is selected:
if Outline1.SelectedItem <= 0 then
compared to:
if TreeView1.Selected=nil then
or
if TreeView1.SelectionCount > 0 then

Well this was just to get you going, but use the relevant Embarcadero DocWiki pages to find out what the various properties and methods do, and go from there.

As also seem in the image, does TOutline work perfectly well and have been maintained to support styles and whatnot, so the urgency of updating and maintaining you code - is not forced on you as with other technologies and frameworks - but it never hurts to keep your code fresh and current - and also throw out all the unused bits :)

/Enjoy

No comments:

Post a Comment