Question

Let me try to explain my situation:

I'm using VirtualTree as a grid, and every time when the OnFocusChanged event is fired, and the active focus node was changed, I have a synchronous operation which may take 0-1 sec which blocks the main Thread (sometimes more).

I can live with this.
For now, I can't move this logic to a worker thread.

My problem happens when I use the arrows keys to move up and down in the grid, and the focus node is changing rapidly. It is not smooth because of the blocking.

So I tried to use a Post message like:

procedure TForm1.VTFocusChanged(Sender: TBaseVirtualTree;
    Node: PVirtualNode; Column: TColumnIndex);
begin
  // if (T = 0) or (GetTickCount - T > 1000) then
  begin
    Sender.InvalidateNode(Node);
    PostMessage(Handle, UM_VT_CHANGED, Integer(Sender), Integer(Node));
  end;
end;

procedure TForm1.UMVTChanged(var Message: TMessage);
var
  Tree: TBaseVirtualTree;
  Node: PVirtualNode;
begin
  // T := GetTickCount;
  Tree := TBaseVirtualTree(Message.WParam);
  Node := PVirtualNode(Message.LParam);
  ...
  // DO the job! 
end;

This helps a little, but still I realize I need some "Idle" mechanism. So if I move arrows up and down my working process will only be triggered when the VirtualTree is Idle for 100 ms or so. I realize I need to use some timer or maybe "eat" the Post messages and process only the last one, but I can't figure how to do it. I tried many variations with TTimer/GetTickCount and it seems I'm really messing something so trivial.

Was it helpful?

Solution

I solved this with a TTimer. Could not think of a better way. Here is the basic idea and it could be improved:

procedure TForm1.VTFocusChanging(Sender: TBaseVirtualTree; OldNode,
  NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex;
  var Allowed: Boolean);
begin
  FocusTimer.Enabled := False;  
end;

procedure TForm1.VTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
begin
  FLastNode := Node;
  FocusTimer.Interval := 250;
  FocusTimer.Enabled := True; 
end;

procedure TForm1.FocusTimer_OnTimer(Sender: TObject);
begin
  FocusTimer.Enabled := False;
  if FLastNode = nil then Exit;
  PostMessage(Handle, UM_VT_CHANGED, Integer(VT), Integer(FLastNode));
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top