Esta pregunta parecerá obvia para aquellos que no han encontrado el problema por sí mismos.VirtualTreeView: manejo adecuado de los cambios de selección
Necesito gestionar los cambios de selección en VTV. Tengo una lista plana de nodos. Necesito hacer cosas con todos los nodos actualmente seleccionados cuando
- El usuario hace clic en un nodo;
- Mayús/Ctrl-clic del usuario en un nodo;
- El usuario utiliza las teclas de flecha para navegar por la lista;
- usuario crea selección arrastrando el ratón
- usuario quita la selección haciendo clic en el espacio vacío o Ctrl-clic en el único nodo seleccionado
etc. Es el comportamiento más común y esperado, al igual que el Explorador de Windows: cuando selecciona archivos con mouse y/o teclado, el panel de información muestra sus propiedades. No necesito nada más que eso. Y aquí es donde me quedo atascado.
A continuación, se detallan algunas de mis investigaciones.
Al principio utilicé OnChange. Parece que ha funcionado bien, pero me di cuenta de un extraño parpadeo y me encontré con que en el escenario más común (se selecciona un nodo, el usuario hace clic en otro) AlCambiar se dispara dos veces:
- Cuando el nodo de edad no está seleccionada . En este momento, la selección está vacía. Actualizo mi GUI para mostrar la etiqueta "no se selecciona nada" en lugar de todas las propiedades.
- Cuando se selecciona el nuevo nodo. Actualizo mi GUI nuevamente para mostrar las propiedades del nuevo nodo. De ahí el parpadeo.
Este problema era googleable, por lo que encontré que la gente usa OnFocusChange y OnFocusChanging en lugar de OnChange. Pero de esta manera solo funciona para una sola selección. Con la selección múltiple, la selección de arrastre y las teclas de navegación, esto no funciona. En algunos casos, los eventos de Enfoque ni siquiera se disparan (por ejemplo, cuando se elimina la selección haciendo clic en el espacio vacío).
Hice algunos estudios de resultados de depuración para aprender cómo se disparan estos controladores en diferentes escenarios. Lo que descubrí es un desastre total sin ningún sentido o patrón visible.
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*!
Esto es bastante difícil de leer. En pocas palabras, dice que dependiendo de la acción específica del usuario, los tres manejadores (OnChange, OnFocusChange y OnFocusChanging) son llamados en orden aleatorio con parámetros aleatorios. FC y FCg a veces nunca se llaman cuando todavía necesito el evento manejado, por lo que es obvio que tengo que usar OnChange.
Pero la siguiente tarea es: dentro de OnChange no sé si debo usar esta llamada o esperar la siguiente. En ocasiones, el conjunto de nodos seleccionado es intermedio y no es útil, y al procesarlo se producirán parpadeos en la GUI y/o cálculos pesados no deseados.
Solo necesito las llamadas marcadas con "!" en la tabla de arriba. Pero no hay forma de distinguirlos del interior. Por ejemplo: si estoy en "C-" (OnChange, Node = nil, SelectedCount = 0) podría significar que el usuario eliminó la selección (entonces tengo que manejarlo) o que hicieron clic en otro nodo (entonces tengo que esperar la próxima llamada OnChange cuando se forma una nueva selección).
De todos modos, espero que mi investigación no sea necesaria. Espero que me esté perdiendo algo que haga que la solución sea simple y clara, y que ustedes, chicos, me lo dirán. Resolver este rompecabezas usando lo que tengo hasta ahora generaría una lógica terriblemente poco fiable y compleja.
¡Gracias de antemano!
Gracias, @TOndrej! Nunca antes había notado esta propiedad. Y no esperaría que existiera tal cosa, para ser honesto. Pero esta parece ser la forma "oficial" de resolver mi problema. Lo probé y funciona, pero me siento un poco incómodo ... resolver esos problemas con temporizadores me parece una muy mala idea. Pero si no surge una solución mejor con el tiempo, tendré que apegarme a esta. – 13x666
@ 13x666, si lo piensas bien, evitar el parpadeo en este caso significa suprimir las actualizaciones de la pantalla si siguen una tras otra "demasiado rápido" ... en cambio, diferir hasta que las cosas (entrada del usuario) "se calmen". –
+1. @ 13x666, un temporizador es en realidad una * solución * muy liviana para esperar a que los usuarios ingresen a "calmarse", como lo expresa TOndrej. Básicamente es solo una llamada a la API de SetTimer. He usado explícitamente temporizadores para este propósito muchas veces, con gran éxito.El usuario no notará la demora de menos de 200 ms, pero el usuario notará parpadeos y demoras en el procesamiento de los comandos posteriores causados por pintar innecesariamente la GUI. –