Skip to content

Commit f8ec48c

Browse files
committed
Fixed issue #820: CheckBox state propagation slow for large number of nodes
1 parent ae85218 commit f8ec48c

File tree

1 file changed

+121
-113
lines changed

1 file changed

+121
-113
lines changed

Source/VirtualTrees.pas

+121-113
Original file line numberDiff line numberDiff line change
@@ -12222,134 +12222,142 @@ function TBaseVirtualTree.ChangeCheckState(Node: PVirtualNode; Value: TCheckStat
1222212222
end;//if
1222312223

1222412224
// Indicate that we are going to propagate check states up and down the hierarchy.
12225-
if FCheckPropagationCount = 0 then // WL, 05.02.2004: Do not enter tsCheckPropagation more than once
12225+
if FCheckPropagationCount = 0 then begin
12226+
// Do not enter tsCheckPropagation more than once
1222612227
DoStateChange([tsCheckPropagation]);
12227-
Inc(FCheckPropagationCount); // WL, 05.02.2004
12228-
// Do actions which are associated with the given check state.
12229-
case CheckType of
12230-
// Check state change with additional consequences for check states of the children.
12231-
ctTriStateCheckBox:
12232-
begin
12233-
// Propagate state down to the children.
12234-
if toAutoTristateTracking in FOptions.FAutoOptions then
12235-
case Value of
12236-
csUncheckedNormal:
12237-
if Node.ChildCount > 0 then
12238-
begin
12239-
Run := FirstChild;
12240-
CheckedCount := 0;
12241-
MixedCheckCount := 0;
12242-
UncheckedCount := 0;
12243-
while Assigned(Run) do
12228+
BeginUpdate();
12229+
end;
12230+
Inc(FCheckPropagationCount);
12231+
try
12232+
// Do actions which are associated with the given check state.
12233+
case CheckType of
12234+
// Check state change with additional consequences for check states of the children.
12235+
ctTriStateCheckBox:
12236+
begin
12237+
// Propagate state down to the children.
12238+
if toAutoTristateTracking in FOptions.FAutoOptions then
12239+
case Value of
12240+
csUncheckedNormal:
12241+
if Node.ChildCount > 0 then
1224412242
begin
12245-
if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
12243+
Run := FirstChild;
12244+
CheckedCount := 0;
12245+
MixedCheckCount := 0;
12246+
UncheckedCount := 0;
12247+
while Assigned(Run) do
1224612248
begin
12247-
if not Self.GetCheckState(Run).IsDisabled() then
12248-
SetCheckState(Run, csUncheckedNormal);
12249-
// Check if the new child state was set successfully, otherwise we have to adjust the
12250-
// node's new check state accordingly.
12251-
case Self.GetCheckState(Run) of
12252-
csCheckedNormal, csCheckedDisabled:
12253-
Inc(CheckedCount);
12254-
csMixedNormal:
12255-
Inc(MixedCheckCount);
12256-
csUncheckedNormal, csUncheckedDisabled:
12257-
Inc(UncheckedCount);
12249+
if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
12250+
begin
12251+
if not Self.GetCheckState(Run).IsDisabled() then
12252+
SetCheckState(Run, csUncheckedNormal);
12253+
// Check if the new child state was set successfully, otherwise we have to adjust the
12254+
// node's new check state accordingly.
12255+
case Self.GetCheckState(Run) of
12256+
csCheckedNormal, csCheckedDisabled:
12257+
Inc(CheckedCount);
12258+
csMixedNormal:
12259+
Inc(MixedCheckCount);
12260+
csUncheckedNormal, csUncheckedDisabled:
12261+
Inc(UncheckedCount);
12262+
end;
1225812263
end;
12264+
Run := Run.NextSibling;
1225912265
end;
12260-
Run := Run.NextSibling;
12261-
end;
1226212266

12263-
// If there is still a mixed state child node checkbox then this node must be mixed checked too.
12264-
if MixedCheckCount > 0 then
12265-
Value := csMixedNormal
12266-
else
12267-
// If nodes are normally checked child nodes then the unchecked count determines what
12268-
// to set for the node itself.
12269-
if CheckedCount > 0 then
12270-
if UncheckedCount > 0 then
12271-
Value := csMixedNormal
12272-
else
12273-
Value := csCheckedNormal;
12274-
end;
12275-
csCheckedNormal:
12276-
if Node.ChildCount > 0 then
12277-
begin
12278-
Run := FirstChild;
12279-
CheckedCount := 0;
12280-
MixedCheckCount := 0;
12281-
UncheckedCount := 0;
12282-
while Assigned(Run) do
12267+
// If there is still a mixed state child node checkbox then this node must be mixed checked too.
12268+
if MixedCheckCount > 0 then
12269+
Value := csMixedNormal
12270+
else
12271+
// If nodes are normally checked child nodes then the unchecked count determines what
12272+
// to set for the node itself.
12273+
if CheckedCount > 0 then
12274+
if UncheckedCount > 0 then
12275+
Value := csMixedNormal
12276+
else
12277+
Value := csCheckedNormal;
12278+
end;
12279+
csCheckedNormal:
12280+
if Node.ChildCount > 0 then
1228312281
begin
12284-
if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
12282+
Run := FirstChild;
12283+
CheckedCount := 0;
12284+
MixedCheckCount := 0;
12285+
UncheckedCount := 0;
12286+
while Assigned(Run) do
1228512287
begin
12286-
if not Self.GetCheckState(Run).IsDisabled() then
12287-
SetCheckState(Run, csCheckedNormal);
12288-
// Check if the new child state was set successfully, otherwise we have to adjust the
12289-
// node's new check state accordingly.
12290-
case Self.GetCheckState(Run) of
12291-
csCheckedNormal:
12292-
Inc(CheckedCount);
12293-
csMixedNormal:
12294-
Inc(MixedCheckCount);
12295-
csUncheckedNormal:
12296-
Inc(UncheckedCount);
12288+
if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
12289+
begin
12290+
if not Self.GetCheckState(Run).IsDisabled() then
12291+
SetCheckState(Run, csCheckedNormal);
12292+
// Check if the new child state was set successfully, otherwise we have to adjust the
12293+
// node's new check state accordingly.
12294+
case Self.GetCheckState(Run) of
12295+
csCheckedNormal:
12296+
Inc(CheckedCount);
12297+
csMixedNormal:
12298+
Inc(MixedCheckCount);
12299+
csUncheckedNormal:
12300+
Inc(UncheckedCount);
12301+
end;
1229712302
end;
12303+
Run := Run.NextSibling;
1229812304
end;
12299-
Run := Run.NextSibling;
12300-
end;
1230112305

12302-
// If there is still a mixed state child node checkbox then this node must be mixed checked too.
12303-
if MixedCheckCount > 0 then
12304-
Value := csMixedNormal
12305-
else
12306-
// If nodes are normally checked child nodes then the unchecked count determines what
12307-
// to set for the node itself.
12308-
if CheckedCount > 0 then
12309-
if UncheckedCount > 0 then
12310-
Value := csMixedNormal
12311-
else
12312-
Value := csCheckedNormal;
12313-
end;
12314-
end;
12315-
end;
12316-
// radio button check state change
12317-
ctRadioButton:
12318-
if Value = csCheckedNormal then
12319-
begin
12320-
Value := csCheckedNormal;
12321-
// Make sure only this node is checked.
12322-
Run := Parent.FirstChild;
12323-
while Assigned(Run) do
12306+
// If there is still a mixed state child node checkbox then this node must be mixed checked too.
12307+
if MixedCheckCount > 0 then
12308+
Value := csMixedNormal
12309+
else
12310+
// If nodes are normally checked child nodes then the unchecked count determines what
12311+
// to set for the node itself.
12312+
if CheckedCount > 0 then
12313+
if UncheckedCount > 0 then
12314+
Value := csMixedNormal
12315+
else
12316+
Value := csCheckedNormal;
12317+
end;
12318+
end;
12319+
end;
12320+
// radio button check state change
12321+
ctRadioButton:
12322+
if Value = csCheckedNormal then
1232412323
begin
12325-
if Run.CheckType = ctRadioButton then
12326-
Run.CheckState := csUncheckedNormal;
12327-
Run := Run.NextSibling;
12324+
Value := csCheckedNormal;
12325+
// Make sure only this node is checked.
12326+
Run := Parent.FirstChild;
12327+
while Assigned(Run) do
12328+
begin
12329+
if Run.CheckType = ctRadioButton then
12330+
Run.CheckState := csUncheckedNormal;
12331+
Run := Run.NextSibling;
12332+
end;
12333+
Invalidate;
1232812334
end;
12329-
Invalidate;
12330-
end;
12331-
end;
12332-
12333-
if Result then
12334-
CheckState := Value // Set new check state
12335-
else
12336-
CheckState := Self.GetCheckState(Node).GetUnpressed(); // Reset dynamic check state.
12337-
12338-
// Propagate state up to the parent.
12339-
if not (vsInitialized in Parent.States) then
12340-
InitNode(Parent);
12341-
if (toAutoTristateTracking in FOptions.FAutoOptions) and ([vsChecking, vsDisabled] * Parent.States = []) and
12342-
(CheckType in [ctCheckBox, ctTriStateCheckBox]) and (Parent <> FRoot) and
12343-
(Parent.CheckType = ctTriStateCheckBox) then
12344-
Result := CheckParentCheckState(Node, Value)
12345-
else
12346-
Result := True;
12335+
end;
1234712336

12348-
InvalidateNode(Node);
12337+
if Result then
12338+
CheckState := Value // Set new check state
12339+
else
12340+
CheckState := Self.GetCheckState(Node).GetUnpressed(); // Reset dynamic check state.
12341+
12342+
// Propagate state up to the parent.
12343+
if not (vsInitialized in Parent.States) then
12344+
InitNode(Parent);
12345+
if (toAutoTristateTracking in FOptions.FAutoOptions) and ([vsChecking, vsDisabled] * Parent.States = []) and
12346+
(CheckType in [ctCheckBox, ctTriStateCheckBox]) and (Parent <> FRoot) and
12347+
(Parent.CheckType = ctTriStateCheckBox) then
12348+
Result := CheckParentCheckState(Node, Value)
12349+
else
12350+
Result := True;
1234912351

12350-
Dec(FCheckPropagationCount); // WL, 05.02.2004
12351-
if FCheckPropagationCount = 0 then // WL, 05.02.2004: Allow state change event after all check operations finished
12352-
DoStateChange([], [tsCheckPropagation]);
12352+
InvalidateNode(Node);
12353+
finally
12354+
Dec(FCheckPropagationCount); // WL, 05.02.2004
12355+
if FCheckPropagationCount = 0 then begin
12356+
// Allow state change event after all check operations finished
12357+
DoStateChange([], [tsCheckPropagation]);
12358+
EndUpdate();
12359+
end;
12360+
end;
1235312361
finally
1235412362
Exclude(States, vsChecking);
1235512363
end;

0 commit comments

Comments
 (0)