I want to achieve the following (using Delphi 12.2):
A TreeView with Checkboxes and two levels (each parent node has two or more child nodes, but no grandchild nodes).
For a node to be partially checked, I must select csPartial
in CheckStyles
. However, now each checkbox (both parent and child checkboxes) cycle through three states while being clicked, whereas they should alternate between checked and unchecked. I can programmatically change the state of a node to partially checked by setting its CheckState
to ncsPartial
, but this only works if CheckStyles
is set to csPartial
, and that results in the three-state cycle.
I realize that I will need to write some code to check or uncheck all child nodes when a parent is checked or unchecked. That is no problem. I just can't figure out how to make a parent node show as partially checked without including csPartial
in CheckStyles
.
I just can't figure out how to make a parent node show as partially checked without including
csPartial
inCheckStyles
.
Unfortunately, you cannot. csPartial
is required.
However, you can use a combination of the TTreeView.OnCheckStateChanging
and TTreeView.OnCheckStateChanged
events to achieve the effect that you want, eg:
type
TMyForm = class(TForm)
TreeView1: TTreeView;
procedure TreeView1CheckStateChanging(Sender: TCustomTreeView;
Node: TTreeNode; NewCheckState, OldCheckState: TNodeCheckState;
var AllowChange: Boolean);
procedure TreeView1CheckStateChanged(Sender: TCustomTreeView;
Node: TTreeNode; CheckState: TNodeCheckState);
private
{ Private declarations }
ChangingNodeStates: Boolean;
public
{ Public declarations }
end;
...
procedure TMyForm.TreeView1CheckStateChanged(Sender: TCustomTreeView;
Node: TTreeNode; CheckState: TNodeCheckState);
begin
if ChangingNodeStates then Exit;
ChangingNodeStates := True;
try
case CheckState of
ncsChecked, ncsUnchecked: begin
if Node.Level = 0 then
begin
for var I := 0 to Node.Count-1 do
begin
Node.Item[I].CheckState := CheckState;
end;
end else
begin
for var I := 0 to Node.Parent.Count-1 do
begin
if Node.Parent.Item[I].CheckState <> CheckState then
begin
Node.Parent.CheckState := ncsPartial;
Exit;
end;
end;
Node.Parent.CheckState := CheckState;
end;
end;
end;
finally
ChangingNodeStates := False;
end;
end;
procedure TMyForm.TreeView1CheckStateChanging(Sender: TCustomTreeView;
Node: TTreeNode; NewCheckState, OldCheckState: TNodeCheckState;
var AllowChange: Boolean);
begin
if (NewCheckState = ncsPartial) and (not ChangingNodeStates) then
begin
AllowChange := False;
Node.CheckState := ncsUnchecked;
end;
end;
The idea being that if the user clicks on a checked or partial node then it will be forced to unchecked, while your code is allowed to set the states to whatever you want whenever a given node changes its state.