VirtualTreeView:正确处理选择更改

13x*_*666 11 delphi user-interface virtualtreeview

对于那些自己没有遇到问题的人来说,这个问题似乎是显而易见的.

我需要处理VTV中的选择变化.我有一个平面的节点列表.我需要随时处理所有当前选定的节点

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

这是最常见和预期的行为,就像Windows资源管理器一样:当您使用鼠标和/或键盘选择文件时,信息面板会显示其属性.我只需要那个.这就是我陷入困境的地方.

我的一些研究如下.


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

  1. 取消选择旧节点时.此时选择为空.我刷新我的GUI以显示"没有选中"标签代替所有属性.
  2. 选择新节点时.我再次刷新我的GUI以显示新节点的属性.因此闪烁.

这个问题是可谷歌的,所以我发现人们使用OnFocusChange和OnFocusChanging而不是OnChange.但这种方式仅适用于单一选择.通过多个选择,拖动选择和导航键,这不起作用.在某些情况下,Focus事件甚至根本不会触发(例如,通过单击空白区域删除选择).

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

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*!
Run Code Online (Sandbox Code Playgroud)

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

但接下来的任务是:在OnChange内部,我不知道是否应该使用此调用或等待下一个调用.有时,所选择的节点集合是中间的且无用的,并且处理它将导致GUI闪烁和/或不需要的大量计算.

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


无论如何,我希望我的研究是不必要的.我希望我错过了一些可以使解决方案变得简单明了的内容,并且你们,伙计们,我会指出它.使用我到目前为止解决这个难题会产生一些非常不可靠和复杂的逻辑.

提前致谢!

Ond*_*lle 12

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

  • +1。@13x666,正如 TOndrej 所说,计时器实际上是一个“非常”轻量级的解决方案,用于等待用户输入“平静下来”。它本质上只是对 SetTimer API 的调用。我已经多次明确地使用计时器来实现此目的,并取得了巨大的成功。用户不会注意到低于 200 毫秒的延迟,但用户会注意到由于不必要地绘制 GUI 而导致处理后续命令时出现闪烁和延迟。 (2认同)