При разработке сложных интерфейсов на управляемых формах 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда на форме размещено несколько таблиц (списков). Типичный сценарий: левый и правый списки, между которыми пользователь постоянно переключается. Однако возникает неприятный эффект — при клике мышкой в соседний список визуальное выделение строки происходит, а программный код в обработчике ПриАктивацииСтроки не выполняется. Это приводит к «залипанию» данных: переменные или связанные поля отображают информацию от предыдущей активной строки.
В этой статье мы подробно разберем, почему платформа ведет себя подобным образом, проанализируем влияние контекстных вызовов на работу интерфейса и рассмотрим несколько надежных способов решения этой проблемы.
Давайте разберем ситуацию с точки зрения ядра платформы 1С. Событие ПриАктивацииСтроки генерируется только тогда, когда внутри конкретного элемента управления (таблицы) изменяется индекс текущей строки. Рассмотрим пошагово проблемный сценарий:
В этот момент фокус возвращается в таблицу, но с точки зрения платформы текущая строка не изменилась. Она как была первой в этом списке, так и осталась. Следовательно, событие активации строки может не инициироваться. Особенно часто это проявляется в версиях платформы 8.3.13 и 8.3.15, где логика обработки фокуса имела свои специфические особенности.
Как показывает практика и эксперименты разработчиков, событие ПриАктивацииЯчейки срабатывает гораздо чаще и стабильнее, чем ПриАктивацииСтроки. Даже если вы используете режим выделения «Строка», платформа все равно отслеживает, в какую именно область (ячейку) был произведен клик мышкой.
Проанализируем пример кода, который позволяет минимизировать пропуски событий:
&НаКлиенте
Процедура ЛевыйСписокПриАктивацииЯчейки(Элемент)
// Используем ТекущиеДанные для получения идентификатора
ДанныеСтроки = Элементы.ЛевыйСписок.ТекущиеДанные;
Если ДанныеСтроки <> Неопределено Тогда
Гл_НомерЗаписи = ДанныеСтроки.НомерЗаписи;
// Дополнительная логика обновления интерфейса
ОбновитьИнформациюОЗаписи(Гл_НомерЗаписи);
КонецЕсли;
КонецПроцедуры
Важно помнить, что активация ячейки происходит при любом клике, даже если строка осталась прежней, что решает проблему возврата фокуса в список.
Одной из скрытых причин «залипания» интерфейса являются неявные серверные вызовы внутри обработчиков активации. Рассмотрим типичную ошибку: программист обращается к реквизитам объекта или вызывает серверную функцию без параметров внутри ПриАктивацииСтроки.
Когда вы выполняете переход между списками, платформа пытается быстро обработать очередь событий. Если в этот момент происходит «тяжелый» серверный вызов, интерфейсный поток блокируется. Платформа может просто «выбросить» следующие события активации из очереди, чтобы сохранить отзывчивость формы. Чтобы проверить это, воспользуйтесь показателями производительности (вкладка «Счетчики») и убедитесь, что количество серверных вызовов при перемещении по строкам равно нулю.
Если серверный вызов необходим, попробуйте реализовать его через обработчик ожидания, чтобы дать форме завершить отрисовку фокуса:
&НаКлиенте
Процедура ЛевыйСписокПриАктивацииСтроки(Элемент)
// Вместо прямого вызова сервера подключаем обработчик на 0.1 секунды
ПодключитьОбработчикОжидания("ВыполнитьОбновлениеДанныхСервер", 0.1, Истина);
КонецПроцедуры
Если ваша логика завязана на конкретный номер записи, который должен быть всегда актуален, рекомендуется дублировать код получения данных в событии ПриПолученииФокуса для каждого из списков. Это гарантирует, что даже если событие активации строки было пропущено платформой из-за отсутствия смены индекса, данные обновятся в момент входа пользователя в область таблицы.
Рассмотрим пример реализации:
&НаКлиенте
Процедура ЛевыйСписокПриПолученииФокуса(Элемент)
// Принудительно вызываем логику активации, так как фокус вернулся в список
ЛевыйСписокПриАктивацииСтроки(Элемент);
КонецПроцедуры
Вместо того чтобы использовать модульные переменные типа Гл_НомерЗаписи и пытаться вручную присваивать им значения в коде, попробуйте использовать штатные механизмы платформы. Если на форме есть поле, которое должно отображать данные текущей строки, вы можете вынести его как Реквизит формы и связать его с путем к данным через Элементы.ЛевыйСписок.ТекущиеДанные.ИмяПоля.
Однако в случае с двумя независимыми списками это работает сложнее. В такой ситуации эффективнее использовать один общий реквизит формы и обновлять его только в событиях. Но помните: избегайте лишних действий в коде. Если значение можно получить напрямую из Элементы.ЛевыйСписок.ТекущиеДанные в тот момент, когда оно реально понадобилось (например, при нажатии кнопки действия), то лучше не хранить его в промежуточных переменных при каждом движении мыши.
Выясним причину зависимости от версии платформы. Начиная с релизов 8.3.16 и выше, работа с фокусом в управляемых формах стала более предсказуемой. Если вы используете версию 8.3.13.1690 (как автор вопроса) и сталкиваетесь с «залипаниями», рассмотрите следующие шаги:
OnActivateRow.ПриАктивацииСтроки, если строка уже была выбрана. Для обработки навигации с клавиатуры использование ПриПолученииФокуса является обязательным.Таким образом, для создания стабильного интерфейса с двумя списками мы рекомендуем комбинировать использование ПриАктивацииЯчейки, полностью исключить контекстные серверные вызовы и добавить обработку получения фокуса элементами управления. Это обеспечит четкую работу программы без необходимости «разблокировать» интерфейс лишними кликами.