При разработке управляемых форм программисты часто сталкиваются с необходимостью обновить данные на форме при перемещении пользователя по списку или таблице. Самый очевидный способ — использовать событие ПриАктивизацииСтроки. Однако именно это событие часто становится причиной аварийного завершения работы программы (дампа платформы). Проанализируем ситуацию, выясним технические причины такого поведения и рассмотрим надежные способы решения задачи.
Рассмотрим типовой сценарий: в форме есть ДинамическийСписок или программно созданная ТаблицаФормы. В модуле формы прописан обработчик события активации строки, который обращается к серверной процедуре с директивой &НаСервере. Платформа может завершить работу без сообщений об ошибках сразу после выполнения процедуры. Рассмотрим причины подробнее.
1. Бесконечная рекурсия событий интерфейса. Это самая частая причина. Когда мы вызываем контекстный серверный метод (&НаСервере), платформа передает весь контекст формы на сервер и возвращает его обратно. После возврата контекста клиентское приложение обязано синхронизировать состояние элементов управления. В процессе этой синхронизации таблица может «перерисоваться» или обновить фокус, что снова инициирует событие ПриАктивизацииСтроки. В итоге получается замкнутый цикл, который переполняет стек вызовов на уровне системных библиотек отрисовки (C++), что и приводит к дампу.
2. Нестабильность программных элементов. Если таблица была создана программно, ее структура в памяти более чувствительна к обновлению контекста. Попытка восстановить состояние прокрутки или фокуса после серверного вызова может привести к обращению по невалидному адресу памяти в графической подсистеме платформы.
3. Прямой запрет в документации. Фирма 1С прямо указывает, что в обработчике ПриАктивизацииСтроки нельзя изменять свойства, которые могут привести к серверному вызову, и не рекомендуется использовать контекстные серверные методы.
Проанализируем самый надежный способ — разрыв потока выполнения. Вместо прямого вызова сервера мы будем «заказывать» этот вызов с небольшой задержкой. Это позволит текущему событию ПриАктивизацииСтроки корректно завершиться, и только после этого выполнится наш код.
Разберем реализацию по шагам:
ПриАктивизацииСтроки подключаем обработчик ожидания.Посмотрим на пример кода:
&НаКлиенте
Перем ТекущийИдентификаторСтроки; // Переменная для кэширования
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
// 1. Проверяем, что строка действительно сменилась
Если ТекущийИдентификаторСтроки = Элемент.ТекущаяСтрока Тогда
Возврат;
КонецЕсли;
ТекущийИдентификаторСтроки = Элемент.ТекущаяСтрока;
// 2. Отключаем старый обработчик (если пользователь быстро листает список)
ОтключитьОбработчикОжидания("Подключаемый_ОбновитьДанныеНаСервере");
// 3. Подключаем новый вызов с минимальной задержкой
ПодключитьОбработчикОжидания("Подключаемый_ОбновитьДанныеНаСервере", 0.1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура Подключаемый_ОбновитьДанныеНаСервере()
// Теперь вызываем сервер безопасно
ОбновитьДанныеНаСервере();
КонецПроцедуры
&НаСервере
Процедура ОбновитьДанныеНаСервере()
// Логика обновления связанных данных
КонецПроцедуры
Если нам необходимо минимизировать задержку, можно использовать флаг выполнения. Этот метод защищает от повторного входа в процедуру, пока предыдущий вызов еще не завершен. Выясним, как это работает: мы создаем переменную формы, которая блокирует выполнение кода внутри события.
&НаКлиенте
Процедура ТаблицаПриАктивизацииСтроки(Элемент)
Если ЭтаФорма.ВыполняетсяОбновление Тогда
Возврат;
КонецЕсли;
ЭтаФорма.ВыполняетсяОбновление = Истина;
Попытка
ВыполнитьСервернуюЗадачу();
Исключение
// Обработка ошибок
КонецПопытки;
ЭтаФорма.ВыполняетсяОбновление = Ложь;
КонецПроцедуры
Важно: Этот способ менее надежен, чем обработчик ожидания, так как он не решает проблему «перерисовки» элементов управления платформой после возврата контекста.
Рассмотрим ситуацию, когда нам нужно получить данные, не изменяя саму форму. В этом случае следует использовать директиву &НаСервереБезКонтекста. Такие вызовы работают значительно быстрее и не вызывают полной синхронизации состояния формы, что минимизирует риск падения платформы.
Проанализируем пример:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
ДанныеСтроки = Элемент.ТекущиеДанные;
Если ДанныеСтроки = Неопределено Тогда
Возврат;
КонецЕсли;
// Передаем только ссылку, не передаем всю форму
ДопИнфо = ПолучитьИнформациюНаСервере(ДанныеСтроки.Ссылка);
// Обновляем только конкретный реквизит на клиенте
Элементы.ДекорацияИнфо.Заголовок = ДопИнфо;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьИнформациюНаСервере(ТоварСсылка)
// Здесь выполняем запрос к БД
Возврат "Остаток: " + ТоварСсылка.Остаток;
КонецФункции
Падение платформы в событии ПриАктивизацииСтроки — это сигнал о том, что интерфейсный поток вошел в конфликт с серверным обменом. Чтобы ваша форма работала стабильно, придерживайтесь следующих правил:
&НаСервере в обработчиках активации. Если серверный вызов необходим, используйте ПодключитьОбработчикОжидания с интервалом 0.1 или 0.2 секунды.&НаСервереБезКонтекста везде, где это возможно. Это экономит трафик и снижает нагрузку на платформу.ТекущаяСтрока изменилась по сравнению с предыдущим вызовом. Это защитит от «дребезга» событий при переключении между окнами.Соблюдение этих рекомендаций позволит создавать отзывчивые и, что самое главное, стабильные интерфейсы, которые не будут пугать пользователей внезапными вылетами программы.