我有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;
但是解决方案存在一些问题。例如,当我使用“取消”按钮关闭表单(表单以ShowModal
方法打开)时,节点被释放,VSTFocusChanged
被触发,后者因为nil按钮而抛出异常。当然,我可以检查按钮是否已分配,但在删除节点后是否有更优雅的解决方案更改焦点而没有这种不良影响(并且没有其他不良影响)?
答案 0 :(得分:4)
是的,有。从这些事件中删除您的代码,并将 toAlwaysSelectNode 选项包含在树视图 TreeOptions.SelectionOptions 选项集中(例如,在IDE中启用它)。此选项的评论说:
如果此标志设置为true,则树视图将尝试始终具有节点 地选择。
这也包括节点删除。
您遇到的问题是您是从 OnFreeNode 事件手动关注节点,而该事件又触发 OnFocusChanged 事件。并且由于节点在控制被销毁时被释放,并且该按钮在之前被销毁,因此您试图访问被破坏的控件。为了避免将来出现这种情况,您可以使用RTL actions,因为即使有 csDestroying 状态信号(包括 OnStructureChange 等事件),VT也会发生很多事件。和行动是一个安全的解决方法。
这样的事情应该安全起作用(我不是行动的粉丝 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操作。