2011-11-03 125 views
11

對於那些自己沒有遇到問題的人來說,這個問題顯而易見。VirtualTreeView:正確處理選擇變化

我需要處理VTV中的選擇更改。我有一個平坦的節點列表。我需要做所有當前選擇的節點的東西,只要

  1. 用戶單擊一個節點;
  2. 用戶按住/ Ctrl鍵單擊一個節點;
  3. 用戶使用箭頭鍵導航列表;
  4. 用戶通過點擊空白處或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調用)。


    無論如何,我希望我的研究是不必要的。我希望我錯過了一些能夠使解決方案簡單明瞭的事情,並且你們會爲我指出問題。使用我迄今爲止所解決的這個難題會產生一些非常不可靠和複雜的邏輯。

    在此先感謝!

  • 回答

    12

    ChangeDelay屬性設置爲適當的大於零的值(以毫秒爲單位),例如, 100。這實現了Rob Kennedy在他的回答中提出的一次性計時器。

    +0

    謝謝,@TOndrej! 我從未注意過此屬性。說實話,我不希望這樣的事情存在。但這似乎是解決我的問題的「官方」方式。 我試了一下,它的工作原理,但感覺有點尷尬......解決定時器這樣的問題對我來說似乎是一個非常糟糕的主意。但如果一段時間沒有更好的解決方案出現,我將不得不堅持這一點。 – 13x666

    +0

    @13x666如果你考慮一下,避免在這種情況下閃爍意味着屏幕更新,如果他們一個接一個「太快」......相反,推遲,直到事情(用戶輸入)「冷靜下來」。 –

    +1

    +1。 @ 13x666,一個計時器實際上是一個非常輕的解決方案,等待用戶輸入「冷靜下來」,正如TOndrej所說的那樣。它本質上只是對SetTimer API的調用。我已經多次明確地將定時器用於此目的,並取得了很大的成功。用戶不會注意到低於200毫秒的延遲,但用戶會注意到由於不必要地繪製GUI而導致處理後續命令時出現閃爍和延遲。 –

    3

    使用一次性定時器。當定時器觸發時,檢查選擇是否不同,如果是,更新顯示器並禁用定時器。每次您收到潛在的選擇更改事件(我認爲這總是OnChange)時,請重置計時器。

    這給你一個等待你真正想要的事件並避免閃爍的方法。成本是一個稍微延遲的用戶界面。

    +0

    感謝您的回答,羅布。我確實在某個時候考慮過這個解決方案,但價格太高。實際上,如果沒有乾淨的解決方案會出現,只需使用每個OnChange將花費更少:閃爍比延遲更容易容忍。不過,這兩種取捨都很難看。 – 13x666

    +0

    對於沒有ChangeDelay屬性的控件,這是要走的路。 –

    0

    我認爲你可能已經使用這裏給出的答案,甚至發現了另一種解決辦法,但我想貢獻一點在這裏...

    在非多選環境(我還沒有在測試它多選環境)我發現了一個非常簡單的解決方案,沒有延遲:

    保留一個全局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執行任何操作,因爲全局指針將與被取消選擇的節點相同。