VirtualTreeView:正确处理选择更改

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

我需要处理VTV中的选择变化。 我有一个平坦的节点列表。 每当我需要对所有当前选定的节点进行操作

  • 用户点击一个节点;
  • 用户移动/按住Ctrl键点击一个节点;
  • 用户使用箭头键导航列表;
  • 用户通过拖动鼠标创建选择
  • 用户通过单击空白空间或按住Ctrl单击仅选择的节点来删除选择
  • 等等。这是最常见和预期的行为,就像Windows资源管理器一样:当您使用鼠标和/或键盘选择文件时,信息面板会显示其属性。 我只需要那些。 这就是我陷入困境的地方。

    我的一些研究如下。


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

  • 旧节点被取消选择时。 这时候的选择是空的。 我刷新我的GUI以显示“没有选择任何东西”标签来代替所有的属性。
  • 当新节点被选中时。 我再次刷新我的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调用)。


    无论如何,我希望我的研究是不必要的。 我希望我错过了一些能够使解决方案简单明了的事情,并且你们会为我指出问题。 使用我迄今为止所解决的这个难题会产生一些非常不可靠和复杂的逻辑。

    提前致谢!


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


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

    这给你一种等待你真正想要的事件并避免闪烁的方式。 成本是一个稍微延迟的用户界面。


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

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

    保持全局PVirtualNode指针(让我们称之为FSelectedTreeNode)。 在启动时显然你会分配零。

    现在eveytime使用箭头键盘按键选择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执行任何操作,因为全局指针将与被取消选择的节点相同。

    链接地址: http://www.djcxy.com/p/63133.html

    上一篇: VirtualTreeView: properly handling selection changes

    下一篇: Delphi: How do i know what my property editor is editing?