Questa domanda sembrerà ovvia a coloro che non hanno riscontrato il problema da soli.VirtualTreeView: gestire correttamente le variazioni di selezione
Devo gestire i cambiamenti di selezione in VTV. Ho una lista di nodi piatta. Ho bisogno di fare cose con tutti i nodi attualmente selezionati ogni volta
- L'utente fa clic su un nodo;
- Maiusc/Ctrl fa clic su un nodo;
- L'utente utilizza i tasti freccia per spostarsi nell'elenco;
- utente crea la selezione trascinando il mouse
- utente rimuove la selezione cliccando su uno spazio vuoto o Ctrl-clic l'unico nodo selezionato
ecc E 'il comportamento più comune e previsto, proprio come Esplora risorse di Windows: quando selezioni i file con mouse e/o tastiera, il pannello delle informazioni mostra le loro proprietà. Non ho bisogno di niente di più. Ed è qui che mi blocco.
Alcune delle mie ricerche seguono.
Inizialmente ho utilizzato OnChange. Sembrava funzionare bene, ma ho notato qualche strano sfarfallio e ho scoperto che nello scenario più comune (è stato selezionato un nodo, l'utente fa clic un altro) OnChange viene attivato due volte:
- Quando il vecchio nodo è deselezionata . In questo momento la selezione è vuota. Aggiorna la mia GUI per mostrare l'etichetta "nulla è selezionato" al posto di tutte le proprietà.
- Quando il nuovo nodo è selezionato. Aggiorna nuovamente la GUI per mostrare le proprietà del nuovo nodo. Da qui lo sfarfallio.
Questo problema era googleable, quindi ho scoperto che le persone utilizzano OnFocusChange e OnFocusChanging anziché OnChange. Ma in questo modo funziona solo per selezione singola. Con la selezione multipla, i tasti di trascinamento e navigazione non funzionano. In alcuni casi gli eventi di messa a fuoco non sparano nemmeno (ad es. Quando la selezione viene rimossa facendo clic su uno spazio vuoto).
Ho fatto uno studio di debug di output per imparare come questi gestori sono licenziati in diversi scenari. Quello che ho scoperto è un casino totale senza alcun senso o schema visibile.
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*!
Questo è piuttosto difficile da leggere. In poche parole dice che a seconda dell'azione specifica dell'utente, i tre gestori (OnChange, OnFocusChange e OnFocusChanging) vengono chiamati in ordine casuale con parametri casuali. FC e FCG a volte non vengono mai chiamati quando ho ancora bisogno dell'evento gestito, quindi è ovvio che devo usare OnChange.
Ma il prossimo compito è: all'interno di OnChange non posso sapere se dovrei usare questa chiamata o aspettare il prossimo. A volte l'insieme dei nodi selezionati è intermedio e non utile e l'elaborazione causerà sfarfallio della GUI e/o calcoli indesiderati.
Ho solo bisogno delle chiamate contrassegnate con "!" nella tabella sopra. Ma non c'è modo di distinguerli dall'interno. Ad esempio: se sono in "C-" (OnChange, Node = nil, SelectedCount = 0) potrebbe significare che l'utente ha rimosso la selezione (quindi devo gestirlo) o che ha fatto clic su un altro nodo (quindi devo aspettare la prossima chiamata OnChange quando si forma una nuova selezione).
In ogni caso, spero che la mia ricerca non sia stata necessaria. Spero di perdere qualcosa che possa rendere la soluzione semplice e chiara e che voi, ragazzi, me lo direte. Risolvere questo enigma usando ciò che ho finora genererebbe una logica terribilmente inaffidabile e complessa.
Grazie in anticipo!
Grazie, @TOndrej! Non ho mai notato questa proprietà prima. E non mi aspetterei che una cosa del genere esista, per essere onesti. Ma questo sembra essere il modo "ufficiale" per risolvere il mio problema. L'ho provato e funziona, ma mi sembra un po 'imbarazzante ... risolvere questi problemi con i timer mi sembra una pessima idea. Ma se nessuna soluzione migliore si presentasse nel tempo, dovrò attenermi a questa. – 13x666
@ 13x666 se ci pensate, evitare lo sfarfallio in questo caso significa sopprimere gli aggiornamenti dello schermo se si susseguono uno dopo l'altro "troppo velocemente" ... invece, rimandare fino a quando le cose (input dell'utente) "si calmano". –
+1. @ 13x666, un timer è in realtà una soluzione * molto * leggera per attendere che l'input dell'utente si "calmi", come dice TOndrej. È essenzialmente solo una chiamata all'API SetTimer. Ho usato esplicitamente i timer per questo scopo molte volte, con grato successo.L'utente non noterà il ritardo di sub-200-ms, ma l'utente noterà lo sfarfallio e ritardi nell'elaborazione dei comandi successivi causati dalla pittura della GUI inutilmente. –