使用TreeNode.MoveTo时如何修复TTreeView错误? [英] how to fix TTreeView bug when using TreeNode.MoveTo?

查看:96
本文介绍了使用TreeNode.MoveTo时如何修复TTreeView错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用TreeNode.MoveTo(...)方法有时无法正常工作,并引发访问冲突异常。



大多数时候都可以工作,有些时候却没有。



大多数情况下,COMCTL32.DLL模块中的访问冲突。读取地址FEEEFEFA'和程序崩溃/冻结。



这是我的代码。

 过程TForm1.FormShow(Sender:TObject); 
var
I,节数:整数;
父母,孩子:TTreeNode;
开始
sectioncount:= 0;
for I:= 0到19做
开始

如果我mod 5 = 0然后
开始
父母:= TreeView1.Items.AddChild( nil,'Section:'+ IntToStr(sectioncount));
Inc(sectioncount);
结尾;

child:= TreeView1.Items.AddChild(父级,‘Child:’+ IntToStr(I));

结尾;

TreeView1.Items [0] .Expand(True);
结尾;

过程TForm1.TreeView1DragDrop(Sender,Source:TObject; X,Y:Integer);
var src,dst:TTreeNode;
I:整数;
开始
dst:= TreeView1.DropTarget;
I的
$ b到TreeView1.SelectionCount-1做
开始
src:= TreeView1.Selections [I];

src.MoveTo(dst,naInsert);
结尾;

结尾;

过程TForm1.TreeView1DragOver(Sender,Source:TObject; X,Y:Integer;
State:TDragState; var Accept:Boolean);
开始
接受:= true;
结尾;

将from从项目添加到项目,添加树视图集树视图dragmode dmAutomatic并多选true。 / p>

,然后



用ctrl按以下顺序选择3个连续节点。
选择中间节点,
选择底部节点,
选择顶部节点。
并将节点的第一个节点拖到另一个位置,您会看到AV错误。



或选择三个节点从上到下并从底部节点AV拖动。



或使用控制键按以下顺序选择三个节点:-首先通过选择选择子1,然后是子2,然后是子0 孩子0

解决方案

一个明显的问题是,当您调用 MoveTo ,则使 for 循环无效。

 对于I:= 0到TreeView1.SelectionCount-1做
开始
src:= TreeView1.Selections [I];
src.MoveTo(dst,naInsert);
结尾;

在调用 MoveTo 之后,您将发现 SelectionCount 不再是您进入循环时的样子。例如,我正在看一个案例,其中循环开始时 SelectionCount 3 ,但为<$第一次调用 MoveTo 之后,c $ c> 1 。这意味着 Selections [I] 的子序列使用是超出范围的。



您需要解决通过记下所选节点然后移动它们来解决该问题。

 过程TForm1.TreeView1DragDrop(Sender,来源: TObject; X,Y:整数); 
var
i:整数;
src,dst:TTreeNode;
nodesToMove:TArray< TTreeNode> ;;
开始
dst:= TreeView1.DropTarget;
SetLength(nodesToMove,TreeView1.SelectionCount);
for i:= 0到高(nodesToMove)做
nodesToMove [i]:= TreeView1.Selections [i];在nodeToMove中为src使用
做src.MoveTo(dst,naInsert);
结尾;






除了这个问题,我还可以重现访问冲突。似乎需要按照非常特殊的顺序移动项目。看来您需要先移动底部节点,然后再移动下一个先前节点,最后移动顶部节点。这段代码似乎可以解决该问题:

 过程TForm1.TreeView1DragDrop(Sender,Source:TObject; X,Y:Integer); 
var
i:整数;
src,dst:TTreeNode;
nodesToMove:TList< TTreeNode> ;;
开始
dst:= TreeView1.DropTarget;
nodesToMove:= TList< TTreeNode> ;.创建;
尝试i的
:= TreeView1.Items.Count-1降至0,如果TreeView1.Items [i]则执行
。然后选择
nodesToMove.Add(TreeView1.Items [i ]);在nodeToMove中为src使用
做src.MoveTo(dst,naInsert);
最终
个NodeToMove.Free;
结尾;
结尾;

虽然不是很令人满意,但很显然我还不了解这里发生的事情。



我现在无法对此进行任何深入研究,但我将答案留在这里,因为我认为这将有助于其他答复者进行更深入的研究。希望有人能够解释AV的运行情况。






好的,我已经更深入了。该问题似乎与 MoveTo 中的代码有关,该代码试图保持要移动的节点的选择状态。 $ b $ $ b $ $ b $ $ b $ $ b $ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$ b

因此,我建议以下变通方法是我所能想到的最好的解决方法:

 过程TForm1.TreeView1DragDrop(Sender,来源:TObject; X,Y:整数); 
var
i:整数;
src,dst:TTreeNode;
nodesToMove:TArray< TTreeNode> ;;
开始
dst:= TreeView1.DropTarget;
SetLength(nodesToMove,TreeView1.SelectionCount);
for i:= 0到高(nodesToMove)做
nodesToMove [i]:= TreeView1.Selections [i];
TreeView1.ClearSelection;在nodeToMove中为src使用

开始
src.MoveTo(dst,naInsert);
TreeView1.Select(src,[ssCtrl]);
结尾;
结尾;

在这里,我们执行以下操作:


  1. 记下所选节点,即需要移动的节点。

  2. 清除选择。

  3. 每个节点移动节点移至新目的地,然后将其移至选定位置。


Using TreeNode.MoveTo(...) method sometimes does not work properly and raises an "Access Violation" exception.

mosttimes works and some time not.

Mosttimes 'Access Violation in module COMCTL32.DLL. Read of address FEEEFEFA' and program crash/freeze.

here is my code.

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;

add a from to a project, add a tree view set tree view dragmode dmAutomatic and multiselect true.

and then

select 3 consecutive node with following order with ctrl. select middle node, select bottom node, select top node. and drag the nodes by first node to another place you can see the AV error.

or select three node top to bottom and drag from bottom node AV arise.

or select three node in following order with control key :- first 'child 1' then 'child 2' then 'child 0' finally drag and drop node by selecting 'Child 0'

解决方案

One clear problem is that when you call MoveTo, you invalidate the for loop.

 for I := 0 to TreeView1.SelectionCount - 1 do
 begin
   src := TreeView1.Selections[I];
   src.MoveTo(dst,naInsert);
 end;

After the call to MoveTo, you will find that SelectionCount is no longer what is was when you entered the loop. For instance, I'm looking at a case here where SelectionCount is 3 when the loop begins, but is 1 after the first call to MoveTo. That means that the subsequence use of Selections[I] are out of bounds.

You'll need to solve the problem by making a note of the selected nodes first, and then moving them.

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;


Beyond that problem, I can reproduce the access violation. It seems that the items needs to be moved in a very particular order. It seems that you need to move the bottom node first, then the next preceding node, and the top node last. This code seems to workaround that problem:

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;

It's not very satisfactory though, and it's clear that I've not yet understood what's going on here.

I cannot look into this any further right now, but I'll leave the answer here as I think it will help other answerers dig deeper. Hopefully somebody will be able to explain what's going on with the AV.


OK, I've dug a bit deeper. The problem appears to be related to the code inside MoveTo that tries to maintain the selection state of the node being moved. I've not yet dug into what the problem is, but it seems to me that you won't be able to do much from the outside to avoid the problem, beyond taking over implementation of selection preserving.

Accordingly I propose the following workaround as the best that I have come up with yet:

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;

Here we do the following:

  1. Make a note of the selected nodes, the nodes that need to move.
  2. Clear the selection.
  3. Move each node to its new destination, and once moved, add that moved node to the selection.

这篇关于使用TreeNode.MoveTo时如何修复TTreeView错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆