12

对于那些自己没有遇到过问题的人来说,这个问题似乎很明显。

我需要处理 VTV 中的选择更改。我有一个简单的节点列表。每当我需要对所有当前选定的节点进行处理

  1. 用户点击一个节点;
  2. 用户 Shift/Ctrl-单击一个节点;
  3. 用户使用箭头键浏览列表;
  4. 用户通过拖动鼠标创建选择
  5. 用户通过单击空白区域或按住 Ctrl 键单击唯一选定的节点来删除选择

等等。这是最常见和预期的行为,就像 Windows 资源管理器一样:当您使用鼠标和/或键盘选择文件时,信息面板会显示它们的属性。我只需要这些。这就是我卡住的地方。

我的一些研究如下。


起初我使用 OnChange。它似乎运行良好,但我注意到一些奇怪的闪烁,我发现在最常见的情况下(选择一个节点,用户单击另一个节点) OnChange 被触发两次:

  1. 取消选择旧节点时。此时选择为空。我刷新我的 GUI 以显示“未选择任何内容”标签来代替所有属性。
  2. 选择新节点时。我再次刷新我的 GUI 以显示新节点的属性。因此闪烁。

这个问题是 googleable,所以我发现人们使用 OnFocusChange 和 OnFocusChanging 而不是 OnChange。但是这种方式只适用于单选。使用多项选择、拖动选择和导航键,这是行不通的。在某些情况下,焦点事件甚至根本不会触发(例如,当通过单击空白区域删除选择时)。

我做了一些调试输出研究来了解这些处理程序在不同场景中是如何被触发的。我发现完全是一团糟,没有任何明显的感觉或模式。

C   OnChange
FC  OnFocusChange
FCg OnFocusChanging
-   nil parameter
*   non-nil parameter
!   valid selection


Nodes     User action                   Handlers fired (in order)
selected                
0     Click node                    FCg-*   C*!     
1     Click same                    FCg**           
1     Click another                 C-  FCg**   C*! FC*
1     Ctlr + Click  same            FCg**   C*!     
1     Ctrl + Click another          FCg**   C*! FC* 
1     Shift + Click same            FCg**   C*!     
1     Shift + Click another         FCg**   C-! FC* 
N     Click focused selected        C-! FCg**       
N     Click unfocused selected      C-! FCg**   FC* 
N     Click unselected              C-  FCg**   C*! FC*
N     Ctrl + Click unselected       FCg**   C*! FC* 
N     Ctrl + Click focused          FCg**   C*!         
N     Shift + Click unselected      FCg**   C-! FC* 
N     Shift + Click focused         FCg**   C-!         
1     Arrow                         FCg**   FC* C-  C*!
1     Shift + Arrow                 FCg**   FC* C*! 
N     Arrow                         FCg**   FC* C-  C*!
N     Shift + Arrow (less)          C*! FCg**   FC* 
N     Shift + Arrow (more)          FCg**   FC* C*! 
Any   Ctrl/Shift + Drag (more)      C*! C-!     
0     Click empty                   -           
1/N   Click Empty                   C-!         
N     Ctrl/Shift + Drag (less)      C-!         
1     Ctrl/Shift + Drag (less)      C-!         
0     Arrow                         FCg**   FC* C*!

这很难阅读。简而言之,它表示根据特定的用户操作,三个处理程序(OnChange、OnFocusChange 和 OnFocusChanging)以随机顺序和随机参数调用。当我仍然需要处理事件时,有时永远不会调用 FC 和 FCg,因此很明显我必须使用 OnChange。

但是下一个任务是:在 OnChange 内部,我不知道是应该使用这个调用还是等待下一个调用。有时选定的节点集是中间的且无用的,处理它会导致 GUI 闪烁和/或不需要的繁重计算。

我只需要标有“!”的电话 在上表中。但是没有办法从内部区分它们。例如:如果我在“C-”(OnChange,Node = nil,SelectedCount = 0)中,这可能意味着用户删除了选择(然后我需要处理它)或者他们点击了另一个节点(然后我需要等待形成新选择时的下一次 OnChange 调用)。


无论如何,我希望我的研究是不必要的。我希望我错过了一些可以使解决方案简单明了的东西,并且你们会为我指出这一点。使用我目前所拥有的解决这个难题会产生一些非常不可靠和复杂的逻辑。

提前致谢!

4

4 回答 4

12

ChangeDelay属性设置为适当的大于零的值(以毫秒为单位),例如100. 这实现了 Rob Kennedy 在他的回答中建议的一次性计时器。

于 2011-11-03T13:58:26.803 回答
3

使用一次性计时器。当计时器触发时,检查选择是否不同,如果是则更新显示,然后禁用计时器。每次您收到潜在的选择更改事件(我认为始终是 OnChange)时,请重置计时器。

这为您提供了一种等待您真正想要的事件并避免闪烁的方法。代价是稍微延迟的 UI。

于 2011-11-03T13:50:16.220 回答
2

您忘记了 OnStateChange 事件。此事件将在任何选择更改后立即触发,然后您可以处理所有选定的节点。

procedure TForm1.vstStateChange(Sender: TBaseVirtualTree; Enter,
  Leave: TVirtualTreeStates);
begin
  if tsChangePending in Leave then
    DoSomething;
end;
于 2019-05-18T10:18:11.667 回答
0

我假设您可能已经使用了这里给出的答案,甚至找到了另一个解决方案,但我想在这里贡献一点......

在非多选环境中(我没有在多选环境中测试过),我发现了一个非常简单的解决方案,没有延迟:

保留一个全局 PVirtualNode 指针(我们称之为 FSelectedTreeNode)。显然,在启动时,您将为其分配 nil。

现在,每次您使用箭头键盘键选择下一个节点时,OnTreeChange 都会发生两次。一次用于将被取消选择的节点,一次用于新选择的节点。在您的 OnTreeChange 事件中,您执行以下操作:

  If Node <> FSelectedTreeNode then
    begin
      FSelectedTreeNode := Node;
      If Node = nil then
        {Do some "Node Deselected" code}
      else
        {Do whatever you want to do when a new node is selected}
    end;

这对我的代码非常有效,它没有闪烁,至少没有延迟。

诀窍是新选择的节点将被分配给全局指针,它会最后发生。因此,当您之后选择另一个节点时,它不会在第一个 OnTreeChange 上执行任何操作,因为这样全局指针将与被取消选择的节点相同。

于 2015-12-27T12:05:01.510 回答