【发布时间】:2016-11-28 14:19:31
【问题描述】:
我在使用 VirtualTreView(v 6.1.0 / Delphi 10 Seattle)时遇到一个(奇怪的)排序问题。我查看了 VTV 的更新版本,没有提到类似的行为。
我将发布完整的源代码,但首先让我解释一下我要完成的工作:
- 我要对一些节点进行排序
- 每个节点都有一个阶段——节点阶段决定了节点的排序顺序(在那个阶段)
- 我希望在通过其阶段排序值选择特定阶段时对节点进行排序。那些未包含在舞台中的节点将被设置为不可见。
这是我正在使用的记录:
PRecord = ^TRecord;
TRecord = record
SortOrder : array [0..4] of integer;
PositionAdded : integer;
end;
添加节点的方法如下:
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;
“阶段”是 SortOrder 索引。 SortOrder[x] := y 表示阶段 X 中节点的顺序应该是 y。 例如,SortOrder[2] = 0 表示在第 2 阶段该节点应该是第一个节点。 SortOrder[3] = -1 表示节点在第 3 阶段“不存在”。
有一个广播组有 5 个元素 (0...4) 呈现舞台。选择阶段后,应根据所选阶段的 SortOrder 值对节点进行排序(我还隐藏了阶段中不存在的节点:
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;
OnCompare 看起来像:
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;
因此,我只比较存在于所选阶段的那些节点(即 SortOrder[stage] -1)。
GetText 显示所选阶段的排序顺序 + 添加到树时节点的原始位置。
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;
现在,当您启动程序并转到(选择单选按钮/单击)到阶段 (3) 时会发生什么,顺序是 1,0 而不是 0,1(我省略了“-1”节点)。
如果在选择阶段 (3) 后选择 (1),则 (3) -> 阶段 3 排序正常。但是然后进入阶段(0),(2),(4),顺序是1,2,0而不是0,1,2。
任何想法为什么这不起作用(如预期的那样)?
我注意到,当两个节点进入 OnCompare 时:如果其中一个节点对于所选阶段的值为 -1,则另一个节点将不再与所选阶段中存在的其余节点进行比较。如何强制其他节点仍然与其他同阶段节点匹配?
【问题讨论】:
-
当
SortOrder是一个节点而不是另一个节点的–1 时,Result是未定义的。比较必须定义一个总排序;如果你没有定义所有个可能的对之间的比较,那么底层的排序算法会给出意想不到的结果,比如在所有节点都被排序之前停止。 -
@Rob,抱歉,结果总是预设为 0(Node1
-
这会产生同样的问题。结果为零表示节点相等。假设您有节点,并且节点 1 设置为 –1。节点 2 将与它进行比较,节点 3 也将与之比较,因为您已跳过比较。通过传递性,这必须意味着节点 2 等于节点 3,但是如果您的比较函数返回不同的答案,那么您没有总排序,并且排序算法可能会失败。
-
是的,有道理,谢谢!
标签: sorting delphi virtualtreeview