Recent

Author Topic: [SOLVED] VirtualStringTree-set Checkbox from Code not propagating back to Parent  (Read 642 times)

Zvoni

  • Hero Member
  • *****
  • Posts: 3303
Hi Folks,
first time using VTV as VirtualStringTree with Checkboxes (CheckType=TriState)
User opens Form, VST is filled from a Database - 3 Levels deep

User can now check/uncheck Nodes as he wishes, with the State of the 2 higher-order nodes being set automagically (as it should be):
All Childnodes checked, "some" childnodes checked (This "Half-State"), no childnodes checked (See screenshot 1)

Now, only checks/unchecks on Level 3 are being saved back to the Database in Real-Time (I'm using the OnChecked-Event)

This works as it should

Now: Next day, user wants to continue, and after the VST is filled, i pull the already checked Level-3-Nodes from the Database.
I iterate over the nodes looking for my identifier, and when found do
Code: Pascal  [Select][+][-]
  1. Node^.CheckState:=csCheckedNormal;
Works as it should

BUT: The newly set CheckState of that Level-3-Node doesn't propagate back to its 2 higher-order Nodes
(Screen shot 2)

What am i missing?
It's not "vst.Refresh;" --> already tried that.
« Last Edit: February 19, 2026, 02:17:09 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

zeljko

  • Hero Member
  • *****
  • Posts: 1875
    • http://wiki.lazarus.freepascal.org/User:Zeljan
It is in your code, do you use vt.BeginUpdate / vt.EndUpdate when populating nodes from db ? Where is code which reacts on checkstate change of node ?

Zvoni

  • Hero Member
  • *****
  • Posts: 3303
It is in your code, do you use vt.BeginUpdate / vt.EndUpdate when populating nodes from db ? Where is code which reacts on checkstate change of node ?
Hi fellow countryman.
No i don't use begin/End-Update


This is my OnChecked-Code.
But it doesn't fire, when i set CheckBox from code (already tested).
Only fires when user clicks
Code: Pascal  [Select][+][-]
  1. procedure TFormCatalogue.vstCompetencesChecked(Sender: TBaseVirtualTree;
  2.   Node: PVirtualNode);
  3. Var
  4.   s:String;
  5.   Level:Integer;
  6.   Q:Integer;
  7. begin
  8.   Level:=StrToInt(vstCompetences.Text[Node,2]);
  9.   If Level=3 Then
  10.     Begin
  11.       Case Node^.CheckState Of
  12.         csCheckedNormal : Begin  //Insert
  13.                             If optPoweruser.Checked Then
  14.                               Q:=SQL_INSERT_POWERUSER_COMPETENCE
  15.                             Else
  16.                               Q:=SQL_INSERT_EMPLOYEE_COMPETENCE;
  17.                           end;
  18.         csUncheckedNormal : Begin //Delete
  19.                               If optPoweruser.Checked Then
  20.                                 Q:=SQL_DELETE_POWERUSER_COMPETENCE
  21.                               Else
  22.                                 Q:=SQL_DELETE_EMPLOYEE_COMPETENCE;
  23.  
  24.                             end;
  25.       End;
  26.       s:=GetSQLStatement(Q);
  27.       SQLQuery.Close;
  28.       SQLQuery.SQL.Clear;
  29.       SQLQuery.SQL.Text:=s;
  30.       SQLQuery.ParamByName('pCID').AsInteger:=StrToInt(vstCompetences.Text[Node,1]);
  31.       SQLQuery.ParamByName('pUID').AsInteger:=UserID;
  32.       SQLQuery.ExecSQL;
  33.       SQLQuery.SQLTransaction.CommitRetaining;
  34.     end;
  35. end;                                                                

EDIT: Using Begin/End-Update before populating/after setting Checkboxes makes no difference.
Meaning
BeginUpdate
Populate VST
Iterate over VST and set Checkboxes where necessary
EndUpdate

No difference

I'm sure i'm missing something simple

EDIT2: I'm not using Event OnChecking whatsoever
(I know that's the event in which you can actually "prevent" a Checkmark being set)

EDIT3: The reason why i would like that "feature":
The Form with VST shows up in collapsed state, so the user actually only sees the (currently) 4 root-Nodes, and it would be like
"In that root-node you already have some selections, in that root-node you have none" etc...
« Last Edit: February 19, 2026, 11:41:11 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

zeljko

  • Hero Member
  • *****
  • Posts: 1875
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Of course it does not trigger when you update Node^.CheckState in code. call vstCompetencesChecked(Sender: TBaseVirtualTree;
  Node: PVirtualNode); whenever you change Node^.CheckState programatically.

Zvoni

  • Hero Member
  • *****
  • Posts: 3303
Of course it does not trigger when you update Node^.CheckState in code. call vstCompetencesChecked(Sender: TBaseVirtualTree;
  Node: PVirtualNode); whenever you change Node^.CheckState programatically.
No cigar.
Had to use a Blocking-Variable "CheckFromCode" within OnChecked to avoid duplicate primary key (Setting a Checkmark invokes an INSERT)
Code: Pascal  [Select][+][-]
  1. procedure TFormCatalogue.vstCompetencesChecked(Sender: TBaseVirtualTree;
  2.   Node: PVirtualNode);
  3. Var
  4.   s:String;
  5.   Level:Integer;
  6.   Q:Integer;
  7. begin
  8.   Level:=StrToInt(vstCompetences.Text[Node,2]);
  9.   If Not CheckFromCode Then
  10.     Begin
  11.       If Level=3 Then
  12.         Begin                            
  13. //My SQL-Stuff INSERTING to Database
  14.  
Code: Pascal  [Select][+][-]
  1. CheckFromCode:=True;
  2. //.....
  3. If Level=3 Then
  4.             Begin
  5.               If Data^.RecordID=SQLQuery.FieldByName(FieldID[Level]).AsString Then
  6.                 Begin
  7.                   Node^.CheckState:=csCheckedNormal;
  8.                   vstCompetencesChecked(vstCompetences,Node);
  9.                   SQLQuery.Next;
  10. //other stuff following that has nothing to do with it
  11.  

*sigh*.... so it's not actually something to do with the Checking itself
Interesting: I found this in TBaseVirtualTree
Code: Pascal  [Select][+][-]
  1. FCheckPropagationCount: Cardinal;            // nesting level of check propagation (WL, 05.02.2004)

and this
Code: Pascal  [Select][+][-]
  1. function TBaseVirtualTree.CheckParentCheckState(Node: PVirtualNode; NewCheckState: TCheckState): Boolean;
  2.  
  3. // Checks all siblings of node to determine which check state Node's parent must get.      
Which actually might look like what i'm after, but the Function is protected, so how is the actual VST calling that?

EDIT: After looking further through the Source, i'm thinking "DoCheckClick" of BaseVirtualTree is the one i need, but how are the Descendants calling that?
« Last Edit: February 19, 2026, 02:01:33 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Zvoni

  • Hero Member
  • *****
  • Posts: 3303
SOLVED (On the german forum)

Code: Pascal  [Select][+][-]
  1. CheckFromCode:=True;
  2. //.....
  3. If Level=3 Then
  4.             Begin
  5.               If Data^.RecordID=SQLQuery.FieldByName(FieldID[Level]).AsString Then
  6.                 Begin
  7.                   Node^.CheckState:=csCheckedNormal;
  8.                   vstCompetencesChecked(vstCompetences,Node);
  9.                   SQLQuery.Next;
Remove Line 8
Replace Line 7 -->
Node^.CheckState:=csCheckedNormal;
with
MyVST.CheckState[Node]:=csCheckedNormal;  (In my Case the MyVST's Name is "vstCompetences" --> vstCompetences.CheckState[Node]:=csCheckedNormal;
This one triggers the Propagation-Check.
NICE!!

Nota bene: I had to use a blocking variable, because the Solution fires the OnChecked-Event, and i have to prevent Duplicate INSERTS.


Reasoning as follows (which in hindsight is completely logical):
Setting the CheckState via
Node^.CheckState:=csCheckedNormal;
sets the State ONLY for the Node --> Its Tree doesn't know about it

But
MyTree.CheckState[Node]:=csCheckedNormal;
tells the tree itself: "Hey, set the Checkmark for this node, and then check everything else"

« Last Edit: February 19, 2026, 02:22:35 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

zeljko

  • Hero Member
  • *****
  • Posts: 1875
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Glad to se you fixed your problem :)

Zvoni

  • Hero Member
  • *****
  • Posts: 3303
and i even removed the need for the Blocking-Variable.

Just changed the INSERT-Statement to "INSERT OR IGNORE blablaha"
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

 

TinyPortal © 2005-2018