В практике разработки на платформе 1С:Предприятие при работе с обычными формами нередко возникает нестандартная ситуация: при программном получении формы через метод ПолучитьФорму() внезапно начинает выполняться событие ПриАктивизацииСтроки или ПриВыводеСтроки для табличного поля. При этом форма еще даже не была визуально открыта, а стандартный обработчик ПриОткрытии() еще не вызывался.
Если в процедуре активизации строки используются переменные модуля формы, инициализируемые только при открытии, система завершает работу с аварийной ошибкой. Причем ошибка часто воспроизводится выборочно — только у одного конкретного пользователя, в то время как у остальных пользователей и на тестовых копиях базы всё открывается штатно. Разберем детально механизм возникновения этой проблемы и рассмотрим все варианты её решения.
Выясним причину, почему событие табличного поля срабатывает раньше, чем код ожидает его увидеть. Для этого проанализируем последовательность инициализации обычной формы в платформе 1С:Предприятие:
ПолучитьФорму() полностью конструирует объект формы и создает все связанные с ней элементы интерфейса в памяти. Однако обработчик ПриОткрытии() вызывается только при непосредственном отображении окна на экране (после вызова Форма.Открыть() или Форма.ОткрытьМодально()). До этого момента модуль формы уже существует, а графические элементы управления уже связаны с источниками данных.ПриАктивизацииСтроки. Если форма содержит сохраненную строку, событие отрабатывает непосредственно внутри вызова ПолучитьФорму().Таким образом, различие в поведении у пользователей обусловлено именно сохраненными настройками: у проблемного пользователя табличное поле оказалось активным элементом в момент сохранения состояния, тогда как у остальных пользователей фокус находился на других элементах либо настройки отсутствовали.
Если конфигурация находится на поддержке без возможности редактирования (типовой программный модуль на замке), проблему можно устранить административными методами путем сброса сохраненных интерфейсных настроек для проблемной учетной записи.
Обработка.CRM_РабочийСтол.Форма.ФормаАРМ).
ИмяПользователяДляСброса = "ИвановИИ";
КлючОбъекта = "Обработка.CRM_РабочийСтол.Форма.ФормаАРМ";
ХранилищеСистемныхНастроек.Удалить(КлючОбъекта, , ИмяПользователяДляСброса);
После удаления сохраненного состояния форма при следующем создании инициализируется с параметрами по умолчанию, активная строка не будет форсированно позиционироваться, и преждевременный вызов обработчика прекратится.
Если в конфигурации предусмотрены специализированные регистры для хранения настроек интерфейса (например, НастройкиПользователей или СохраненныеНастройки), необходимо также проверить и очистить соответствующие записи по проблемному пользователю.
Если конфигурация снята с поддержки или дорабатывается с помощью механизма расширений, наиболее надежным решением является устранение зависимости обработчика от порядка открытия формы. Стандарты разработки 1С предписывают не полагаться на то, что ПриОткрытии() выполнится раньше событий табличных полей.
Рассмотрим, как правильно защитить обработчик от обращения к неинициализированным данным:
// В начале модуля формы объявляем переменную:
Перем мФормаИнициализирована;
Процедура ЗадачаСписокПриАктивизацииСтроки(Элемент)
// Прерываем выполнение, если форма еще не открыта и переменные не заполнены
Если Не мФормаИнициализирована Тогда
Возврат;
КонецЕсли;
// Основной алгоритм обработки строки
ОбработатьВыборЗадачи();
КонецПроцедуры
Процедура ПриОткрытии()
// Инициализация основных переменных и структур данных
ИнициализироватьДанныеФормы();
// Разрешаем выполнение обработчиков элементов управления
мФормаИнициализирована = Истина;
КонецПроцедуры
// В теле модуля (вне процедур) задаем начальное значение
мФормаИнициализирована = Ложь;
Проанализируем альтернативный вариант, когда переменные требуются табличному полю сразу в момент построения формы. Если данные списка зависят от некоторых служебных структур (массивов соответствий, настроек отборов), перенесем их заполнение из процедуры ПриОткрытии() во внепроцедурную часть модуля или в обработчик ПередОткрытием().
ПередОткрытием() также вызывается раньше события ПриОткрытии(), однако следует учитывать, что при простом вызове ПолучитьФорму() без последующего открытия оно также не сработает до момента вызова метода Открыть(). Поэтому первичные глобальные переменные самой формы надежнее всего инициализировать именно в теле модуля:
// Тело модуля формы (выполняется непосредственно при вызове ПолучитьФорму)
мСписокПараметров = Новый Структура;
мСписокПараметров.Вставить("РежимРаботы", 1);
мСписокПараметров.Вставить("ТекущийСтатус", Неопределено);
Если логика в событии ПриАктивизацииСтроки требует тяжелых расчетов, обращения к базе данных или синхронизации с другими элементами окна, отложим её выполнение до момента, пока пользовательский интерфейс полностью не отобразится на экране. Для этого применим однократный обработчик ожидания:
Процедура ЗадачаСписокПриАктивизацииСтроки(Элемент)
// Сбрасываем предыдущий вызов, если пользователь быстро перемещается по списку
ОтключитьОбработчикОжидания("ВыполнитьАктивизациюСтрокиОтложено");
// Подключаем однократный вызов с минимальной задержкой
ПодключитьОбработчикОжидания("ВыполнитьАктивизациюСтрокиОтложено", 0.1, Истина);
КонецПроцедуры
Процедура ВыполнитьАктивизациюСтрокиОтложено()
// Данный код выполнится уже после полной отрисовки окна и завершения всех процедур открытия
Если ЭлементыФормы.ЗадачаСписок.ТекущаяСтрока <> Неопределено Тогда
ОбновитьПодчиненныеДанные(ЭлементыФормы.ЗадачаСписок.ТекущиеДанные);
КонецЕсли;
КонецПроцедуры
Такой подход полностью устраняет конфликт последовательности вызовов в платформе 1С:Предприятие 8.3 и гарантирует корректную работу формы у всех пользователей независимо от сохраненных настроек интерфейса.