I'm having a (strange) sorting problem with VirtualTreView (v 6.1.0 / Delphi 10 Seattle). I've looked into more recent versions of VTV and there's no mention of a similar behavior.
I'll post full source code, but let me first explain what I'm trying to accomplish:
- There are nodes I'd like to sort
- Each node has a kind of stage - where the node stage determines the sort order of the node (in that stage)
- I'd like to have nodes sorted when a particular stage is selected by their stage sort value. Those nodes that are not included in the stage are to be set invisible.
Here's the record I'm using:
PRecord = ^TRecord;
TRecord = record
SortOrder : array [0..4] of integer;
PositionAdded : integer;
end;
Here's how nodes are added:
procedure TVTVSortForm.FormCreate(Sender: TObject);
var
vn : PVirtualNode;
pr : PRecord;
begin
tree.NodeDataSize := SizeOf(TRecord);
vn := tree.AddChild(nil);
pr := PRecord(tree.GetNodeData(vn));
pr.PositionAdded := vn.Index;
pr.SortOrder[0] := 0; //first in 0 - removed in later
pr.SortOrder[1] := -1;
pr.SortOrder[2] := -1;
pr.SortOrder[3] := -1;
pr.SortOrder[4] := 2; //third in 4
vn := tree.AddChild(nil);
pr := PRecord(tree.GetNodeData(vn));
pr.PositionAdded := vn.Index;
pr.SortOrder[0] := 1; //second in 0
pr.SortOrder[1] := 0; //first in 1
pr.SortOrder[2] := 1; //second in 2
pr.SortOrder[3] := 1; //second in 3
pr.SortOrder[4] := 0; //first in 4
vn := tree.AddChild(nil);
pr := PRecord(tree.GetNodeData(vn));
pr.PositionAdded := vn.Index;
pr.SortOrder[0] := 2; // third in 0
pr.SortOrder[1] := 2; //third in 1
pr.SortOrder[2] := 0; //first in 2
pr.SortOrder[3] := -1; //removed in 3
pr.SortOrder[4] := 1; //second in 4
vn := tree.AddChild(nil);
pr := PRecord(tree.GetNodeData(vn));
pr.PositionAdded := vn.Index;
pr.SortOrder[0] := -1; //not in 0
pr.SortOrder[1] := 1; //second in 1
pr.SortOrder[2] := 2; //third in 2
pr.SortOrder[3] := 0; // first in 3
pr.SortOrder[4] := -1; // not in 4
tree.ValidateNode(nil, true);
end;
"Stages" are SortOrder indices. SortOrder[x] := y means that the order of the node in stage X should be y. For example, SortOrder[2] = 0 means that in stage 2 the node should be first node. SortOrder[3] = -1 means that the node "does not exist" in stage 3.
There's a radiogroup having 5 elements (0...4) presenting the stage. When the stage is selected the nodes should be sorted according their SortOrder value for the stage selected (I'm also hiding nodes that are not existing in a stage:
procedure TVTVSortForm.rgFilterAndSortClick(Sender: TObject);
begin
tree.Sort(tree.RootNode, 0, sdAscending);
tree.IterateSubtree(
nil,
procedure (Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean)
var
d : PRecord;
begin
d := PRecord(tree.GetNodeData(Node));
//tree.IsVisible[Node] := -1 <> d.SortOrder[rgFilterAndSort.ItemIndex];
end,
nil);
end;
The OnCompare looks like:
procedure TVTVSortForm.treeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var
d1, d2 : PRecord;
begin
d1 := PRecord(Sender.GetNodeData(Node1));
d2 := PRecord(Sender.GetNodeData(Node2));
if (-1 <> d1.SortOrder[rgFilterAndSort.ItemIndex]) AND (-1 <> d2.SortOrder[rgFilterAndSort.ItemIndex]) then
result := d1.SortOrder[rgFilterAndSort.ItemIndex] - d2.SortOrder[rgFilterAndSort.ItemIndex];
end;
Therefore I'm only comparing those nodes that exist in the selected stage (i.e. SortOrder[stage] <> -1).
The GetText displays the sort order for the selected stage + the original position of the node at the time of adding to the tree.
procedure TVTVSortForm.treeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
Data: PRecord;
begin
Data := Sender.GetNodeData(Node);
CellText := Format('s:%d, p:%d', [Data.SortOrder[rgFilterAndSort.ItemIndex], Data.PositionAdded]);
end;
Now, what happens when you start the program and go (select radio button/click) to stage (3), the order is 1,0 instead of 0,1 (I'm omitting "-1" nodes).
If after selecting stage (3) you select (1) then (3) -> stage 3 is sorted ok. But then go to stage (0),(2),(4) and the order is 1,2,0 instead of 0,1,2.
Any ideas why this is not working (as expected)?
I've noted that when two nodes go into OnCompare: if one of the nodes has -1 for the selected stage, the other node will not any more be compared to the remaining nodes that exist in the selected stage. How to force the other node to still be matched with other same-stage nodes?