我有一个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按钮而引发异常.当然,我可以检查按钮是否已分配,但是在删除节点后是否有更优雅的解决方案来更改焦点而没有这种不良影响(并且没有其他不良影响)?
解决方法
是否有内置方法可以始终选择节点?
就在这里.从这些事件中删除您的代码,并将toAlwaysSelectNode选项包含在树视图TreeOptions.SelectionOptions选项集中(例如,在IDE中启用它).此选项的评论说:
If this flag is set to true,the tree view tries to always have a node
selected.
如何从VT事件安全地更新外部控件的启用状态?
您面临的问题是您是从OnFreeNode事件手动聚焦节点,而OnFreeNode事件又触发了OnFocusChanged事件.并且由于节点在控制被销毁时也被释放,并且该按钮在之前被销毁,因此您试图访问被破坏的控件.为了在将来避免这种情况,您可以使用RTL actions,因为即使有cs描述状态信号(包括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动作.