Этот вопрос будет казаться очевидным для тех, кто сам не сталкивался с проблемой.
Мне нужно обрабатывать изменения выбора в VTV. У меня есть плоский список узлов. Мне нужно делать вещи со всеми выбранными в данный момент узлами всякий раз, когда
- Пользователь нажимает кнопку node;
- Пользователь Shift/Ctrl-clicks node;
- Пользователь использует клавиши со стрелками для навигации по списку;
- Пользователь создает выбор, перетаскивая мышью
- Пользователь удаляет выделение, нажимая на пустое пространство или нажав Ctrl, только выбранный node
и т.д.. Это наиболее распространенное и ожидаемое поведение, как и проводник Windows: при выборе файлов с помощью мыши и/или клавиатуры информационная панель показывает свои свойства. Мне не нужно ничего больше. И здесь я застреваю.
Некоторые из моих исследований следует.
Сначала я использовал OnChange. Казалось, что это хорошо работает, но я заметил странное мерцание, и я обнаружил, что в наиболее распространенном сценарии (один node выбран, пользователь нажимает другой) OnChange запускается дважды:
- Если старый node не выбран. В это время выбор пуст. Я обновляю свой графический интерфейс, чтобы вместо ярлыков вместо ярлыков отображалась метка "ничего не выбрано".
- Когда выбран новый node. Я снова обновляю свой графический интерфейс, чтобы показать свойства нового node. Отсюда мерцание.
Эта проблема была 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 я не могу знать, должен ли я использовать этот вызов или ждать следующего. Иногда набор выбранных узлов является промежуточным и непригодным, и обработка его вызовет мерцание графического интерфейса и/или нежелательные тяжелые вычисления.
Мне нужны только вызовы, отмеченные знаком "!". в таблице выше. Но нет способа отличить их отнутри. Например: если я нахожусь в "C-" (OnChange, node= nil, SelectedCount = 0), это может означать, что пользователь удалил выделение (тогда мне нужно его обработать) или что они нажали на другой node (тогда я необходимо подождать следующего вызова OnChange при создании нового выбора).
В любом случае, я надеюсь, что мои исследования не нужны. Я надеюсь, что я упустил что-то, что сделало бы решение простым и понятным, и что вы, ребята, захотите указать мне на это. Решение этой головоломки с использованием того, что у меня было до сих пор, создало бы ужасно ненадежную и сложную логику.
Спасибо заранее!