12

この質問は、問題に遭遇したことがない人にとっては明らかです。

VTV で選択の変更を処理する必要があります。ノードのフラット リストがあります。いつでも現在選択されているすべてのノードで何かをする必要があります

  1. ユーザーがノードをクリックします。
  2. ユーザー Shift/Ctrl キーを押しながらノードをクリックします。
  3. ユーザーは矢印キーを使用してリストをナビゲートします。
  4. ユーザーがマウスをドラッグして選択範囲を作成
  5. ユーザーが空のスペースをクリックするか、選択されたノードのみを Ctrl キーを押しながらクリックして、選択を削除します。

これは、Windows エクスプローラーと同様に、最も一般的で予想される動作です。マウスやキーボードでファイルを選択すると、情報パネルにそのプロパティが表示されます。それ以上は必要ありません。そして、これは私が立ち往生するところです。

私の研究のいくつかは次のとおりです。


最初は OnChange を使用しました。うまく機能しているように見えましたが、奇妙なちらつきに気付き、最も一般的なシナリオ (1 つのノードが選択され、ユーザーが別のノードをクリックする) で OnChange が 2 回発生することがわかりました。

  1. 古いノードが選択解除されたとき。この時点では、選択は空です。GUI を更新して、すべてのプロパティの代わりに「何も選択されていません」というラベルを表示します。
  2. 新しいノードが選択されたとき。GUI を再度更新して、新しいノードのプロパティを表示します。したがって、ちらつき。

この問題はググることができたので、人々は OnChange の代わりに OnFocusChange と OnFocusChanging を使用していることがわかりました。ただし、この方法は単一選択でのみ機能します。複数選択、ドラッグ選択、およびナビゲーション キーでは、これは機能しません。場合によっては、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*!

これはかなり読みにくいです。簡単に言うと、特定のユーザー アクションに応じて、3 つのハンドラー (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 に答える