我听过很多关于VirtualTreeView组件的赞誉,并且正在考虑在我们正在进行的重写中使用它。目前我们使用StringGrid。
我无法找到对多列进行排序的方法,尽管单列排序效果很好。是否有任何方法可以执行类似于单击第1列>排序,按Ctrl +单击第2列>在第1列之后对第2列进行排序等等?
具体来说,我想排序至少三列:PO编号,行项目,发布。
提前感谢您的帮助!
以下是我正在测试理论的代码(稍微简化)(不是来自上面引用的相同项目):
注意:更新后,我也编辑了我的代码,以显示目前的代码。下面我发布了排序结果:
type
PBatchDetails = ^TBatchDetails;
TBatchDetails = record
TheBatchKey
OperationKey,
PO,
Line,
Release,
Temp,
Notes : String;
TransDate : TDateTime;
end;
....
Sorting_Columns: array of TColumnIndex;
....
procedure TForm1.TreeHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
var
I: Integer;
begin
if not CtrlDown then //function I have to test Ctrl state.
begin
setlength(Sorting_Columns,0);
end;
SetLength(Sorting_Columns,length(Sorting_Columns)+1);
Sorting_Columns[Length(Sorting_Columns)-1] := HitInfo.Column;
tree.SortTree(HitInfo.Column,Sender.SortDirection,True);
if Sender.SortDirection=sdAscending then
Sender.SortDirection:=sdDescending
else
Sender.SortDirection:=sdAscending
end;
procedure TForm1.TreeCompareNodes(Sender: TBaseVirtualTree; Node1,
Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var
BatchRec1 : PBatchDetails;
BatchRec2: PBatchDetails;
I: Integer;
begin
if length(Sorting_Columns) > 0 then
begin
BatchRec1 := Tree.GetNodeData(Node1);
BatchRec2 := Tree.GetNodeData(Node2);
if (not Assigned(BatchRec1)) or (not Assigned(BatchRec2)) then
Result:=0
else
begin
for I := High(Sorting_Columns) downto 0 do
begin
case Sorting_Columns[i] of
0,1: Result := Result + CompareDate(BatchRec1.TransDate,BatchRec2.TransDate); // col 0 is Date and col 1 is Time.
2: Result := Result + CompareText(BatchRec1.OperationKey,BatchRec2.OperationKey);
3: Result := Result + CompareText(BatchRec1.PO,BatchRec2.PO);
4: Result := Result + CompareText(BatchRec1.Line,BatchRec2.Line);
5: Result := Result + CompareText(BatchRec1.Release,BatchRec2.Release);
6: Result := Result + CompareText(BatchRec1.Temp, BatchRec2.Temp);
7: Result := Result + CompareText(BatchRec1.Notes,BatchRec2.Notes);
end; //end case;
if Result <> 0 then
Break;
end;
end;
end;
end;
这产生了以下结果(我只显示了我想在这里排序的三列):
最初加载时:
PO线释放
153 7 2
153 7 1
153 1 1
153 1 2
153 4 1
153 6 2
153 6 1
120 3 2
120 3 1
153 2 1
153 4 2
120 2 1
153 4 1
120 1 1
153 3 1
153 2 1
111 2 1
111 1 5
111 1 1
111 4 2
111 3 1
111 4 1
111 1 3
111 1 2
111 1 4
首次点击后
PO线释放
111 2 1
111 1 5
111 1 1
111 4 2
111 3 1
111 4 1
111 1 3
111 1 2
111 1 4
120 3 2
120 3 1
120 2 1
120 1 1
153 7 2
153 7 1
153 1 1
153 1 2
153 4 1
153 6 2
153 6 1
153 2 1
153 4 2
153 4 1
153 3 1
153 2 1
第二次点击后
PO线释放
153 7 2
153 7 1
153 6 2
153 6 1
153 4 1
153 4 2
153 4 1
111 4 2
111 4 1
153 3 1
120 3 2
120 3 1
111 3 1
153 2 1
153 2 1
120 2 1
111 2 1
153 1 1
153 1 2
120 1 1
111 1 5
111 1 1
111 1 3
111 1 2
111 1 4
三次点击后
PO线释放
111 1 1
120 1 1
153 1 1
111 2 1
120 2 1
153 2 1
153 2 1
111 3 1
120 3 1
153 3 1
111 4 1
153 4 1
153 4 1
153 6 1
153 7 1
111 1 2
153 1 2
120 3 2
111 4 2
153 4 2
153 6 2
153 7 2
111 1 3
111 1 4
111 1 5
谢谢你的时间!
答案 0 :(得分:2)
一般禁用每个自动排序选项。然后,您需要实现OnCompareNodes以及OnHeaderClick事件。
这是我希望工作代码(我做了快速测试:)
目的是将排序列存储在某个变量中(Sorting_Columns)。您可以在OnHeaderClick事件中提供此变量。
在OnCompareNodes事件中,将在SortTree函数调用之后触发,从最后添加的列到第一个添加的列迭代变量,并向Result参数传递第一个非零值比较结果。现在人性化 - 你应该向后选择列,因为它们被“选中”并检查它们是否相同,如果是,则转到之前选择的,如果没有中断循环并传递结果。
请注意,您在一次事件命中中比较两个节点(行),迭代的原因是什么,以及后续比较列的比较。
type
PRecord = ^TRecord;
TRecord = record
ID: integer;
Text_1: string;
Text_2: string;
Text_3: string;
Date: TDateTime;
end;
...
var Sorting_Columns: array of TColumnIndex;
...
procedure TForm1.VirtualStringTree1CompareNodes(Sender: TBaseVirtualTree;
Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var Actual_Index: integer;
Data_1: PRecord;
Data_2: PRecord;
begin
if Length(Sorting_Columns) > 0 then
begin
Data_1 := VirtualStringTree1.GetNodeData(Node1);
Data_2 := VirtualStringTree1.GetNodeData(Node2);
if Assigned(Data_1) and Assigned(Data_2) then
for Actual_Index := High(Sorting_Columns) downto 0 do
case Sorting_Columns[Actual_Index] of
0: Result := Result + Data_1^.ID - Data_2^.ID;
1: Result := Result + CompareStr(Data_1^.Text_1, Data_2^.Text_1);
2: Result := Result + CompareStr(Data_1^.Text_2, Data_2^.Text_2);
3: Result := Result + CompareStr(Data_1^.Text_3, Data_2^.Text_3);
4: Result := Result + CompareDateTime(Data_1^.Date, Data_2^.Date);
end;
if Result <> 0 then
Break;
end;
end;
答案 1 :(得分:0)
@ user532231的代码略有修改,以获得有效的解决方案
type
PRecord = ^TRecord;
TRecord = record
ID: integer;
Text_1: string;
Text_2: string;
Text_3: string;
Date: TDateTime;
end;
...
var Sorting_Columns: array of TColumnIndex;
...
procedure TForm1.VirtualStringTree1CompareNodes(Sender: TBaseVirtualTree;
Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var Actual_Index: integer;
Data_1: PRecord;
Data_2: PRecord;
Matrix : array of integer;
I: Integer;
begin
if Length(Sorting_Columns) > 0 then
begin
Data_1 := VirtualStringTree1.GetNodeData(Node1);
Data_2 := VirtualStringTree1.GetNodeData(Node2);
if Assigned(Data_1) and Assigned(Data_2) then
begin
SetLength(Matrix,Length(Sorting_Columns));
for Actual_Index := 0 to High(Sorting_Columns) do
begin
case Sorting_Columns[Actual_Index] of
0: Matrix[Actual_Index] := Data_1^.ID - Data_2^.ID;
1: Matrix[Actual_Index] := CompareStr(Data_1^.Text_1, Data_2^.Text_1);
2: Matrix[Actual_Index] := CompareStr(Data_1^.Text_2, Data_2^.Text_2);
3: Matrix[Actual_Index] := CompareStr(Data_1^.Text_3, Data_2^.Text_3);
4: Matrix[Actual_Index] := CompareDateTime(Data_1^.Date, Data_2^.Date);
end;
end;
for I := 0 to Length(Matrix) - 1 do
if (Matrix[i] <> 0) then
begin
Result:=Matrix[i];
break;
end;
SetLength(Matrix,0);
end;
end;
end;
区别在于,您需要记住每个列比较的结果,然后返回第一个最高有效的非零值(最高有效是首先添加到排序的列)。您无需从最高到最低的列循环。此代码需要OP的TreeHeaderClick过程将列添加/删除到Sorting_Columns中。
在此,所有列的排序方向始终相同。通过按列的升序或降序反转各列的比较结果,实现排序方向应该相当容易。我没有尝试过。