使用TreeNode.MoveTo(...)方法有时无法正常工作并引发"访问冲突"异常.
大多数时候有效,有些时候没有.
大多数时候'模块COMCTL32.DLL中的访问冲突.读取地址FEEEFEFA'并程序崩溃/冻结.
这是我的代码.
procedure TForm1.FormShow(Sender: TObject);
var
I, sectioncount: Integer;
parent, child: TTreeNode;
begin
sectioncount := 0;
for I := 0 to 19 do
begin
if I mod 5 = 0 then
begin
parent := TreeView1.Items.AddChild(nil, 'Section: ' + IntToStr(sectioncount));
Inc(sectioncount);
end;
child := TreeView1.Items.AddChild(parent, 'Child: ' + IntToStr(I));
end;
TreeView1.Items[0].Expand(True);
end;
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var src, dst : TTreeNode;
I : Integer;
begin
dst := TreeView1.DropTarget;
for I := 0 to TreeView1.SelectionCount - 1 do
begin
src := TreeView1.Selections[I];
src.MoveTo(dst,naInsert);
end;
end;
procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := true;
end;
Run Code Online (Sandbox Code Playgroud)
添加一个from到一个项目,添加一个树视图集树视图dragmode dmAutomatic和multiselect true.
然后
使用ctrl选择具有以下顺序的3个连续节点.选择中间节点,选择底部节点,选择顶部节点.并将第一个节点的节点拖到另一个可以看到AV错误的地方.
或从上到下选择三个节点并从底部节点AV拖动出现.
或者用控制键按以下顺序选择三个节点: - 首先'子1'然后'子2'然后'子0'最后通过选择'子0'拖放节点
一个明显的问题是,当您调用时MoveTo,您将使for循环无效.
for I := 0 to TreeView1.SelectionCount - 1 do
begin
src := TreeView1.Selections[I];
src.MoveTo(dst,naInsert);
end;
Run Code Online (Sandbox Code Playgroud)
在调用之后MoveTo,您会发现SelectionCount当您进入循环时不再是这样.例如,我在这里看一个案例,SelectionCount即3循环开始时,但是1在第一次调用之后MoveTo.这意味着后续使用Selections[I]是超出界限的.
您需要首先记下所选节点,然后再移动它们来解决问题.
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
i: Integer;
src, dst: TTreeNode;
nodesToMove: TArray<TTreeNode>;
begin
dst := TreeView1.DropTarget;
SetLength(nodesToMove, TreeView1.SelectionCount);
for i := 0 to high(nodesToMove) do
nodesToMove[i] := TreeView1.Selections[i];
for src in nodesToMove do
src.MoveTo(dst, naInsert);
end;
Run Code Online (Sandbox Code Playgroud)
除了这个问题,我可以重现访问冲突.似乎需要以非常特定的顺序移动项目.您似乎需要先移动底部节点,然后移动下一个前一个节点,最后移动顶部节点.此代码似乎解决了这个问题:
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
i: Integer;
src, dst: TTreeNode;
nodesToMove: TList<TTreeNode>;
begin
dst := TreeView1.DropTarget;
nodesToMove := TList<TTreeNode>.Create;
try
for i := TreeView1.Items.Count-1 downto 0 do
if TreeView1.Items[i].Selected then
nodesToMove.Add(TreeView1.Items[i]);
for src in nodesToMove do
src.MoveTo(dst, naInsert);
finally
nodesToMove.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
但这并不是很令人满意,而且我很清楚我还没有理解这里发生了什么.
我现在无法进一步研究这个问题,但我会在这里留下答案,因为我认为这将有助于其他回答者深入挖掘.希望有人能够解释AV的情况.
好的,我挖得更深了一点.该问题似乎与MoveTo试图维持被移动节点的选择状态的内部代码有关.我还没有深入研究问题所在,但在我看来,除了接管选择保留的实施之外,你无法从外部做很多事情来避免这个问题.
因此,我提出以下解决方案作为我提出的最佳方法:
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
i: Integer;
src, dst: TTreeNode;
nodesToMove: TArray<TTreeNode>;
begin
dst := TreeView1.DropTarget;
SetLength(nodesToMove, TreeView1.SelectionCount);
for i := 0 to high(nodesToMove) do
nodesToMove[i] := TreeView1.Selections[i];
TreeView1.ClearSelection;
for src in nodesToMove do
begin
src.MoveTo(dst, naInsert);
TreeView1.Select(src, [ssCtrl]);
end;
end;
Run Code Online (Sandbox Code Playgroud)
我们在这里做以下事情:
| 归档时间: |
|
| 查看次数: |
1348 次 |
| 最近记录: |