0

I have Virtual Treeview and I use OnClick to execute code, when user clicks on node. In order for the same code is executed also when user uses keyboard to move from node to node, I use OnFocusChanged. In OnFocusChanged I call OnClick, so it is always same code executed, of course.

So, when a node is selected and user clicks on another node, both events are called OnClick and OnFocusChanges... and since OnFocusChanged calls OnClick, I use a little trick to avoid double executino of OnClick. I use flag to set to ignore 2nd call of OnClick.

Here is the code - OnFocusChanged:

procedure TForm1.VTVFocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
begin
  gTVFocusHasJustChanged_SkipClickEvent := false; // enable executing  OnClick code
  VTVClick(Sender);
  gTVFocusHasJustChanged_SkipClickEvent := True; // disable executing OnClick code
end;

And here is OnClick:

procedure TForm1.VTVClick(Sender: TObject);
var
  Data: ^rTreeData;
begin
  // skip second OnClick call!
  if gTVFocusHasJustChanged_SkipClickEvent then
  begin
    gTVFocusHasJustChanged_SkipClickEvent := false;
    Exit;
  end;

  ... // code to be executed when node selected

end;

This works as expected, either user clicks on node or moves with keyboard.

BUT, Is there better way to do this, easier to maintain or just makes more sense?


EDIT:

I think I need to add a little more info. Only specific code is executed when user selects node. Either with mouse, keyboard, if it was focused or not, already selected one node before and now selecting new one... basically in any and all occurrences, only specific code needs to be executed. I chose to use OnClick method where I have this code, I could use another function that gets called from OnClick, but that is practically the same. I do not execute different code based on condition of what happened, either it was a click, it was a keyboard move... all the same, no distinction between where the code was fired from. I hope this makes more sense of what is going on and provides more info where I need help.

EDIT #2:

Update on my progress: I started moving all my code from OnClick into separate procedures, which improves maintainability and easier execution while node is selected (Mouse click or with keyboard) or under other circumstances. The answer below and comments gave me nudge into right direction that OnClick is not the right place where code that is not based on OnClick event should be executed.

Mike Torrettinni
  • 1,816
  • 2
  • 17
  • 47
  • A) Don't call OnClick from OnFocusChanged. That seems like a massive code smell as there is no click occurs. B) Move your `code to be executed when node selected` into a new function and try to figure things out from there. – Shannon Matthews Nov 22 '15 at 20:44
  • Hm, if i understand correctly, the new function would be called from OnClick and the newly discovered 'on node select'. I would need to use probably similar flag to avoid double-execution. Right? – Mike Torrettinni Nov 22 '15 at 21:02
  • @MikeTorrettinni You say that clicking on Virtual Tree View triggers both `OnClick` and `OnFocusChanged` events. So why don't you have all of your code in `OnFocusChanged` event instead? From your question description it seems that you code needs to execute only when selected node is changed and not on every click. So `OnFocusChange` event would be more appropriate to use for this. Wouldn't it be? – SilverWarior Nov 22 '15 at 23:01
  • @SilverWarior it works good, except in case where node is already selected and focus is moved away from TV (click on another control), then selecting (clicking) on same node again it doesn't trigger OnFocusChanged, sincer it didn't really change nodes, but OnClick is executed this time. – Mike Torrettinni Nov 22 '15 at 23:28
  • I don't have Virtual TreeView installed. Does it perhaps have `OnEnter` and `OnExit` events like many other controls have. If it does then you should better use `OnEnter` event to fire the `OnFocusChanged` event instead of `OnClick` event. You would also need to use `OnEnter` event in order to detect when Virtual TreeView got focus through the use of TAB Key. I also believe this even is usually fired certain control gets focus after your application becoming active again. – SilverWarior Nov 23 '15 at 00:21
  • Yes, OnEnter is fired first (when entered back into TV focus) and then OnFocusChanged, if another node is selected. So it would work when selecting same node, but would need the flag to avoid duplicate execution of OnFocusChanged when OnEnter and selecting another node, so OnFocusChanged would be caled twice. Different way, but same problem. – Mike Torrettinni Nov 23 '15 at 01:04
  • @MikeTorrettinni why do you need to perform OnFocusChanged to the same selected node on OnEnter event? I use TreeviewNode on almost all my works and i just need to use OnFocusChanged. But if you "have to" maybe use a global selectedNode would be better? – Theo Nov 23 '15 at 01:48
  • Sometimes I have 2 TVs and they both show details in same Memo, depending which one is selected last or currently). So when clicking between these 2 TVs, the correct details are shown in Memo. If TV1.OnEnter would not call TV1.OnFocusChanged, then when selecting same node as it is already selected in TV1, the old details (from previous selected TV2) would be still seen in Memo. – Mike Torrettinni Nov 23 '15 at 02:04
  • @MikeTorrettinni Ok, i got your point. Maybe this sample code below would give you some ideas.. – Theo Nov 23 '15 at 02:17

1 Answers1

3

The main idea is use global selected Node for every VirtualTree.

Here is the code :

  TForm1 = class(TForm)
    ..........
  private
    fselectedVTNode : PVirtualNode;   // used as global selected node for VTV
    ..........
  public
    ..........
  end;


//========== Event for VTV.OnFocusChanged ===============
procedure TForm1.VTVFocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
var
  Data: ^rTreeData;
begin
  if (fselectedVTNode <> Node) then begin
    fselectedVTNode := Node;

    // ....... code to be executed when node selected

  end;
end;

//========== Event for VTV.OnFreeNode ===============
procedure TForm1.VTVFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
begin
  if fselectedVTNode = Node then
    fselectedVTNode := nil; 

  // .......code to be executed when node freed
end;

//========== Event for VTV.OnEnter ===============
procedure TForm1.VTVEnter(Sender: TObject);
var
  VT:TBaseVirtualTree;
begin
  fselectedVTNode := nil; 
  VT:=TBaseVirtualTree(Sender); 
  VTVFocusChanged(VT, VT.FocusedNode, VT.FocusedColumn);
end;
Theo
  • 454
  • 1
  • 7
  • 21
  • Thank you! This seems cleaner. I have around 150 VTs, I will convert couple of them and see where it gets me. I want cleaner way than what I have, but don't want too complicated. I will report get back to you when I see test a few with new code. – Mike Torrettinni Nov 23 '15 at 03:16
  • I tried and it works. It is a simple solution ,but I was hoping for even simpler, smaller - less methods involved. Right now I have a bit 'odd' implementation, but it works with only 2 methods. – Mike Torrettinni Nov 24 '15 at 02:37