使用TreeNode.MoveTo(...)方法有时无法正常工作并引发“访问冲突”#34;例外。
大部分时间都有效,有些时候没有。
大多数情况下'访问模块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;
添加一个from到项目,添加树视图集树视图dragmode dmAutomatic和multiselect true。
然后
使用ctrl按顺序选择3个连续节点。 选择中间节点, 选择底部节点, 选择顶级节点。 并将第一个节点的节点拖到另一个可以看到AV错误的地方。
或从上到下选择三个节点并从底部节点AV中拖出。
或使用控制键按以下顺序选择三个节点: - first' child 1'然后'孩子2'然后'孩子0'最后通过选择' Child 0'
来拖放节点答案 0 :(得分:5)
一个明显的问题是,当您致电MoveTo
时,您会使for
循环失效。
for I := 0 to TreeView1.SelectionCount - 1 do
begin
src := TreeView1.Selections[I];
src.MoveTo(dst,naInsert);
end;
致电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;
除了这个问题,我可以重现访问冲突。似乎需要以非常特定的顺序移动项目。您似乎需要先移动底部节点,然后移动下一个前一个节点,最后移动顶部节点。此代码似乎解决了这个问题:
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;
虽然不太令人满意,而且我很清楚我还不知道这里发生了什么。
我现在无法进一步研究这个问题,但我会在这里留下答案,因为我认为这将有助于其他回答者深入挖掘。希望有人能够解释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;
我们在此处执行以下操作:
答案 1 :(得分:3)
这是您不需要采取行动的公共控件库抛出的第一个机会异常。它可能是一个bug或一个故意的例外,在任何一种情况下都没有什么可追求的,例外由图书馆本身处理得很好。
Delphi调试器可能在处理异常时遇到问题。通过我的XE2测试,当我在“调试器异常通知”中选择“继续”时,我希望程序继续正常运行。但是,异常对话框会中断程序执行。但是,在调试器外部运行没有问题,您将看不到任何中断移动操作的对话框。
请注意,这仅与您提供的复制步骤之一(最后一个)相关。与其他人一样,RTL引发的“列表越界”异常是由您的代码引起的,您需要更正。
答案 2 :(得分:1)
我修复了在 Delphi 10.4.2 中按住 Shift 键拖放多个节点时出现的错误
from sklearn.externals import joblib
joblib.dump(model, 'sentiment_model.pkl')