performancedelphitreeviewcustom-draw

Delphi TTreeView OnCustomDrawItem event slows it down


I have an outliner application (in Delphi 10.2 Tokyo) that I use for taking notes (I'll call it NoteApp). I have another application that I use for editing plain text (TextApp). Since I switch between these applications a lot, I decided to integrate note-taking abilities within TextApp.

I copy/pasted the code from NoteApp to TextApp, and I put the components (one TTreeView, one TRichEdit, and one TActionToolbar) on TextApp.Form_Main . OnCustomDrawItem event of the TTreeView is set to change FontStyle of each Node based on the NoteType of the corresponding note item which is an array of simple records:

type

  ///
  ///  Note types
  ///
  TNoteType = (ntNote, ntTodo, ntDone, ntNext, ntTitle) ;
  ///
  ///
  ///
  TNote = Record
    Text       ,
    Attachment ,
    Properties ,
    CloseDate  : String      ;
    NoteType   : TNoteType   ;
  End;

Our array:

var
  Notes: Array of TNote ;

And the event:

procedure TForm_Main.TreeView_NotesCustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  ///
  ///  First check to see if the application allows us to change visuals. If the
  ///  application is in processing mode, visual updates are not allowed.
  ///
  if ChangeAllowed AND Node.IsVisible then
    begin
      ///
      ///  Check NoteType of the corresponding note:
      ///
      case Notes[Node.AbsoluteIndex].NoteType of
        ntTitle:
          begin
            TreeView_Notes.Canvas.Font.Style := [fsBold] ;
          end;
        //ntNote:
        //  begin
        //  end;
        ntTodo:
          begin
            TreeView_Notes.Canvas.Font.Style := [fsBold] ;
          end;
        ntNext:
          begin
            TreeView_Notes.Canvas.Font.Style := [fsUnderline] ;
          end;
        ntDone:
          begin
            TreeView_Notes.Canvas.Font.Style := [fsStrikeOut] ;
          end;
      end;
    end;
end;

When I open a note file in NoteApp, it works perfectly. When I open the same file in TextApp, TTreeView refreshes slowly. The top items in the TTreeView are alright, but the lower you go, the lower the refreshing rate.
The properties of all of the components are identically set. I suspect I have made a mistake somewhere. I set the visibility of all of the other components on TextApp to false, but the TTreeView is still abysmally slow. If I remove the code above, it becomes fast again. I don't use runtime themes in TextApp.


Solution

  • Okay, I found the answer to the question.

    The answer was hidden in the code above, and what I posted was enough to answer the question. Turns out the code that I have posted was MCVE after all. I am posting the answer in case it happens to someone else.

    Answer:

    Turns out Node.AbsoluteIndex is incredibly slow. It shouldn't be used as an index.

    Solution 1:

    I used Node.Data as an index, and now it is very fast.

    Solution 2:

    An alternative solution that I tried and worked:

      TTreeNodeNote = class(TTreeNode)
      public
        Note: TNote;
      end;
    
    procedure TForm_Main.TreeView_NotesCreateNodeClass(Sender: TCustomTreeView;
      var NodeClass: TTreeNodeClass);
    begin
      NodeClass := TTreeNodeNote;
    end;
    

    Then we store the data in the Note property of each Node, instead of a separate array. Works like a charm.