如何解决TVirtualStringTree的RootNode的父级
根据文档,TVirtualStringTree的属性RootNode是一个隐藏节点,是所有用户创建的节点的父节点。但是已设置RootNode的父级。我面临以下问题:
有时,当关闭包含TVirtualStringTree的表单时,出现访问冲突错误。调试时,尝试访问组件的名称属性(虚拟字符串树)时发生错误。如果我尝试评估Name属性,则会得到“无法访问的值”。深入研究问题,我发现组件cannot be changed in runtime的Name属性:
警告:在运行时更改名称会导致对旧名称的任何引用 名称变得不确定。任何使用旧名称的后续代码 将导致异常。
碰巧我没有在代码中更改它。在Name属性中使用数据断点,我看到它在virtualstringtree循环中发生了变化,就像这样(我在示例应用程序中用于测试问题的示例代码,但生产代码与此类似):
var
N: PNode;
P: PVirtualNode;
begin
P := tree.FocusedNode;
while Assigned(P) do
begin
N := tree.GetNodeData(P);
P := P.Parent;
end;
GetNodeData如下:
if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
Result := nil
else begin
Result := PByte(@Node.Data) + FTotalInternalDataSize;
Include(Node.States,vsOnFreeNodeCallRequired); // We now need to call OnFreeNode,see bug #323
end;
鉴于我有以下树:
Node 1
Node 2
Node 3
选择节点3时,循环执行5次。 3在我创建的节点中,然后是RootNode(隐藏),然后是根的父级。仅父级根节点的“父级”为nil。在GetNodeData方法中验证它是否是根节点,返回nil。但是对于根的此父级(因为它不是根),请输入else代码。现在是问题所在:
virtualstrintree的Name属性的地址为$ 141B9768
根节点的父级的States属性的地址为$ 141B976A
将数据断点置于Name属性中时,它会在此时更改:
Include(Node.States,vsOnFreeNodeCallRequired);
并生成AV。
我知道我可以更改循环以检查根节点correctly,但是我想了解这一点,所以我可以修复组件(如果是组件错误),而不是我的代码。 / p>
代码:
type
TNode = record
Text: string;
end;
PNode = ^TNode;
...
procedure TForm1.FormCreate(Sender: TObject);
var
N1,N2,N3: TNode;
TreeNode: PVirtualNode;
begin
N1.Text := 'Node 1';
TreeNode := tree.AddChild(nil,PNode(N1));
N2.Text := 'Node 2';
TreeNode := tree.AddChild(TreeNode,PNode(N2));
N3.Text := 'Node 3';
TreeNode := tree.AddChild(TreeNode,PNode(N3));
end;
只需在表单中放置一棵树和一个按钮,然后单击按钮即可放入循环。
解决方法
首先,说明文件:(例如) {{3}}
说(强调我的意思)
属性RootNode:PVirtualNode;
说明
用于锚定树 维护内部树节点的层次结构 与其他任何树节点一样,但有时处理方式有所不同。的 根节点始终被扩展和初始化。 其上级会员积分 到节点所属的树视图及其PreviousSibling 和NextSibling成员指向根节点本身 可能会真正识别出该节点。
注释
您不应使用根节点来遍历树。它 只能公开访问,因为它是所有顶层的父级 节点,可用于测试节点是顶级节点还是节点 不是。
第二
在您的代码中
<ItemsControl Grid.Row="2" x:Name="MessagesItemsCtrl" Grid.Column="1" Margin="0,5,0" ItemsSource="{Binding NewChatter}" ItemTemplate="{DynamicResource MessagesDataTemplate}" AllowDrop="True" Drop="MessagesItemsCtrl_Drop"
ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer x:Name="myscroll" ScrollChanged="MessagesItemsCtrl_ScrollChanged">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
您无条件地向上遍历内部根节点,因为您没有通过与var
N: PNode;
P: PVirtualNode;
begin
P := tree.FocusedNode;
while Assigned(P) do
begin
N := tree.GetNodeData(P);
P := P.Parent;
end;
(或与P
比较)来检查tree.RootNode
是内部根。
也许改变
P.NextSibling
条件
while Assigned(P) do
会适合你
,因此,由于汤姆的回答以及组件的文档说明,RootNode的父级实际上是树本身。因此,当以Parent = nil
作为停止点进行循环时,到达父级的根(树)时,将执行Include
中的代码GetNodeData
,在我的情况下,当访问State
属性,它的地址与树的名称地址非常接近,因为“节点”的地址就是树。
因此,我修复了循环以检查Node <> Tree.RootNode
,还修复了GetNodeData
以检查Node是否为Self:or (Node = Pointer(Self)
,以防万一我的其他代码应用程序(具有200万以上代码的旧版)使用相同的错误循环:
之前:
function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer;
begin
Assert(FNodeDataSize > 0,'NodeDataSize not initialized.');
if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
Result := nil
else begin
Result := PByte(@Node.Data) + FTotalInternalDataSize;
Include(Node.States,vsOnFreeNodeCallRequired); // We now need to call OnFreeNode,see bug #323
end;
end;
之后:
function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer;
begin
Assert(FNodeDataSize > 0,'NodeDataSize not initialized.');
if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) or (Node = Pointer(Self)) then
Result := nil
else begin
Result := PByte(@Node.Data) + FTotalInternalDataSize;
Include(Node.States,see bug #323
end;
end;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。