Chr*_*s S 8 delphi focus virtualtreeview tvirtualstringtree
我有一个类型的组件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;
Run Code Online (Sandbox Code Playgroud)
我还希望更改引起一些反应,例如设置Enabled按钮的属性:
procedure TMyForm.VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
begin
btn1.Enabled := Assigned(Node);
end;
Run Code Online (Sandbox Code Playgroud)
但是解决方案存在一些问题.例如,当我使用"取消"按钮关闭表单(表单是使用ShowModal方法打开)时,节点被释放,VSTFocusChanged被触发,并且由于nil按钮,后者会抛出异常.当然,我可以检查按钮是否已分配,但在删除节点后是否有更优雅的解决方案更改焦点而没有这种不良影响(并且没有其他不良影响)?
就在这里。从这些事件中删除代码,并将toAlwaysSelectNode选项包含到树视图TreeOptions.SelectionOptions选项集中(例如,在 IDE 中启用它)。这个选项的评论说:
如果此标志设置为 true,树视图将尝试始终选择一个节点。
这也包括节点删除。
您面临的问题是您从OnFreeNode事件手动聚焦节点,这又触发了OnFocusChanged事件。并且由于当控件被销毁并且该按钮之前被销毁时节点也会被释放,因此您试图访问被销毁的控件。为了避免将来出现这种情况,您可以使用RTL 操作,因为即使 VT 已发出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;
Run Code Online (Sandbox Code Playgroud)
无需 RTL 操作,您可以安全地更新该按钮状态,例如在执行操作后立即更新,例如在删除节点后:
VST.DeleteNode(VST.FocusedNode);
ButtonDeleteNode.Enabled := VST.SelectedCount > 0;
Run Code Online (Sandbox Code Playgroud)
但这样你就可能会失去同步并编写更多代码。因此,我更喜欢使用 RTL 操作。