Adding OnChangeExpanded Functionality to TTreeView

Under the VCL, TTreeView has events OnExpanded, OnExpanding, OnCollapsing and OnCollapsed but there’s nothing similar in FireMonkey.

TTreeViewItem has a number of methods which get called when the expanded state changes, but none of these is virtual, and therefore there’s no simple patch to add such support. However the IsExpanded property does change to reflect the state and since this is a trigger property is is possible to hack the styling and animation systems to add the support we need.

Simply put we need to add an animation to the item, give it a duration of 0 (so it fires immediately) and hook into it’s OnFinish event so we know when the animation has run. It doesn’t matter which animation we use so I plumped for a TFloatAnimation, but it does need a valid PropertyName so I used Opacity and used a couple of near identical values so the change shouldn’t be noticed.

There’s one slight fly in the ointment. When IsExpanded changes the only animations run are those of the ‘button’ object from the style, so rather than adding the animation directly to the TTreeViewItem you need to add it to the ‘button’ item extracted from the style by FindStyleResource.

So, we create a custom child of TTreeViewItem and add the above action to ApplyStyle. From there it’s simple to add a suitable event, OnChangeExpanded, and a method to call it.

The unit for the new tree view item is,

unit TreeViewExpanded;

interface
uses FMX.TreeViewClasses;

type TTreeViewItemExpanded = class(TTreeViewItem)
  private
    
FOnChangeExpandedTNotifyEvent;
  protected
    
procedure ApplyStyle;override;
    
procedure DoChangeExpanded(SenderTObject);
  
published
    property OnChangeExpanded
TNotifyEvent read FOnChangeExpanded write FOnChangeExpanded;
end;

implementation
uses FMX
.AniFMX.Types;

{ TTreeViewItemExpanded }

procedure TTreeViewItemExpanded
.ApplyStyle;
var 
AniTFloatAnimation;
  
OTFMXObject;
begin
  inherited
;

  
:= FindStyleResource('button');
  if 
Assigned(Othen
  begin
    Ani 
:= TFloatAnimation.Create(O);
    
Ani.Parent := O;
    
Ani.Stored := False;
    
Ani.StartValue := 0.999999999999;
    
Ani.StopValue := 1;
    
Ani.PropertyName := 'Opacity';
    
Ani.Trigger := 'IsExpanded=true';
    
Ani.TriggerInverse := 'IsExpanded=false';
    
Ani.Duration := 0;
    
Ani.OnFinish := DoChangeExpanded;
  
end;
end;

procedure TTreeViewItemExpanded.DoChangeExpanded(SenderTObject);
begin
  
if Assigned(OnChangeExpandedthen
    OnChangeExpanded
(Self);
end;

end

And here’s a bit of code from the app to test things out,

procedure TForm1.EVChangeExpanded(SenderTObject);
var 
ItemTTreeViewItem;
begin
  
if Sender is TTreeViewItem then
  begin
    Item 
:= Sender as TTreeViewItem;
    if 
Item.IsExpanded then
      Item
.Text := 'Expanded'
    
else
      
Item.Text := 'Collapsed';
  
end;

end;

procedure TForm1.FormCreate(SenderTObject);
var 
ItemTTreeViewItemExpanded;
begin
  Item 
:= TTreeViewItemExpanded.Create(Self);
  
TreeView1.AddObject(Item);
  
Item.OnChangeExpanded := EVChangeExpanded;
  
Item.Text := 'Parent';
  
Item.AddObject(TTreeViewItemExpanded.Create(Self));
  
Item.Items[0].Text := 'Child';
end
Commenting is not available in this channel entry.