Delphi:从虚拟字符串树中删除节点后更改焦点

8

我有一个类型为TVirtualStringTree(以下简称VST)的组件。它的节点以列表形式呈现,即所有节点都在同一级别上。我希望在删除节点后更改焦点(使用DeleteNode方法),我使用了OnFreeNode事件:

procedure TMyForm.VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  NewFocus: PVirtualNode;
begin
  NewFocus := VST.GetNext(Node);
  if not Assigned(newFocus) then
    NewFocus := VST.GetPrevious(Node);
  if Assigned(NewFocus) then
  begin
    VST.FocusedNode := NewFocus;
    VST.Selected[NewFocus] := True
  end;
end;

我希望这个改变能够引起一些反应,例如设置按钮的Enabled属性:

procedure TMyForm.VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
begin
  btn1.Enabled := Assigned(Node);
end;

但是这种解决方案存在一些问题。例如,当我使用Cancel按钮关闭表单时(该表单是使用ShowModal方法打开的),节点被释放,VSTFocusChanged被触发,后者因为空按钮而抛出异常。当然,我可以检查按钮是否已分配,但是否有更优雅的解决方案在删除节点后更改焦点而不会产生这种不希望的效果(以及其他不希望的效果)?

1个回答

5

是否有内置方法始终选择一个节点?

是的,有。从这些事件中删除您的代码,并将 toAlwaysSelectNode 选项包含在树视图 TreeOptions.SelectionOptions 选项集中(例如在IDE中启用它)。此选项的注释如下:

如果将此标志设置为true,则树视图尝试始终选择节点。

这也包括节点删除。


如何安全地更新来自VT事件的外部控件的启用状态?

您面临的问题是您手动从 OnFreeNode 事件聚焦节点,从而触发了 OnFocusChanged 事件。由于节点在控件被销毁时也会被释放,而该按钮在之前被销毁,因此您正在尝试访问已被销毁的控件。为了避免将来出现此问题,您可以使用 RTL操作,因为即使在信号 csDestroying 状态下,VT也会触发很多事件(包括像 OnStructureChange 这样的事件),操作是一种安全的解决方法。

像这样的东西应该能够安全工作(我不喜欢操作 OnUpdate 事件):

procedure TMyForm.VSTStructureChange(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Reason: TChangeReason);
begin
  { ActionDeleteNode is assigned to the button's Action property; SelectedCount
    is a bit paranoic here because if you use the toAlwaysSelectNode option, at
    least one node should be always selected, so RootNodeCount > 0 could do the
    same here }
  ActionDeleteNode.Enabled := Sender.SelectedCount > 0;
end;

没有 RTL 操作,您可以安全地更新该按钮状态,例如在执行操作后立即更新,例如在删除节点后:
VST.DeleteNode(VST.FocusedNode);
ButtonDeleteNode.Enabled := VST.SelectedCount > 0;

但是如果您不使用RTL操作,可能会导致代码发生变化和增加。因此,我更喜欢使用RTL操作。


如果已分配(ButtonDeleteNode) then...谁/什么保证在销毁时将ButtonDeleteNode设置为nil?这是另一件OP应该注意的事情。 - kobik
1
好的观点。我没有考虑到这一点。我只是从问题中希望VCL销毁控件并取消分配其引用。但在帮助文件中没有找到相关说明。我会将其删除。 - Victoria
再次强调,ButtonDeleteNode 可能已经在 OnStructureChange 中被释放。因此,我反对使用此事件来更新“操作”。您需要使用一些标志,以便可以退出此事件。 - kobik
1
我反对笨拙的 OnUpdate 操作事件。从那里更新操作是安全的(但访问“外部”控件不安全)。 - Victoria

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接