При разработке и сопровождении конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с аппаратной особенностью (так называемой «фичей») движка платформы. Суть проблемы заключается в том, что формула, назначенная колонке табличной части (ТЧ) со ссылочным типом данных, стабильно срабатывает дважды. Первый раз — непосредственно в момент интерактивного выбора элемента из справочника, а второй раз — при потере фокуса этой ячейкой (например, при нажатии клавиши Enter, переходе на другую строку или клике мышью).
Особенно неприятно это поведение проявляется в тех случаях, когда внутри исполняемой процедуры вызываются модальные диалоги, задаются вопросы пользователю через функцию Вопрос() или выполняются тяжелые транзакционные расчеты. Пользователю приходится дважды отвечать на один и тот же вопрос, что сильно замедляет работу и вызывает раздражение.
В этой статье мы подробно проанализируем физику этого процесса и разберем несколько надежных способов обхода данной особенности, начиная от простых флагов и заканчивая перехватом стандартной обработки выбора.
Для того чтобы эффективно бороться с проблемой, необходимо понять внутреннюю механику работы интерфейса 1С 7.7. Когда пользователь работает с ячейкой табличной части документа, процесс выбора значения делится на два этапа:
Теперь, когда логика работы платформы ясна, рассмотрим практические методы решения этой проблемы.
Это самый простой способ, который не требует переписывания структуры метаданных и интерфейса. Мы можем контролировать однократность выполнения кода с помощью переменной-флага на уровне модуля формы документа.
Разберем этот алгоритм по шагам:
ПриНачалеРедактированияСтроки() будем сбрасывать этот флаг в значение 0 при каждом переходе на новую строку или начале ее изменения.1. При повторном срабатывании формулы (при потере фокуса) процедура увидит, что флаг равен 1, и мгновенно завершит работу без повторения кода.Посмотрим на пример программной реализации этого метода:
// Объявляем переменную в самом начале модуля формы документа
Перем ФлагПартияВыбрана;
Процедура ПриНачалеРедактированияСтроки()
// Сбрасываем флаг при начале редактирования любой строки ТЧ
ФлагПартияВыбрана = 0;
КонецПроцедуры
Процедура ПриВыбореПартии()
// Проверяем, выбран ли элемент и не обрабатывался ли он ранее в этой строке
Если (ФлагПартияВыбрана = 0) И (Материал.Выбран() = 1) Тогда
// Взводим флаг, чтобы предотвратить второй вызов при потере фокуса
ФлагПартияВыбрана = 1;
// Наш полезный код
Если Вопрос("Обновить характеристики выбранного материала?", "Да+Нет") = "Да" Тогда
ТекМатериал = СоздатьОбъект("Справочник.Материалы");
Если ТекМатериал.НайтиЭлемент(Материал) = 1 Тогда
// Производим необходимые действия с объектом справочника
ТекМатериал.Записать();
Форма.Обновить(); // Принудительно обновляем визуальное отображение формы
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот метод прост в реализации и отлично подходит для большинства типовых задач конфигураций 1С 7.7.
Иногда использование предопределенной процедуры ПриНачалеРедактированияСтроки() нежелательно или невозможно (например, если логика работы ТЧ сложна и завязана на динамическое добавление строк программными методами). В этом случае можно обойтись сравнением текущего значения с сохраненным предыдущим значением реквизита.
Для этого мы также объявляем переменную модуля формы для хранения старого значения:
Перем ПредыдущееЗначениеМатериала;
Процедура ПриВыбореПартии()
// Если текущее значение в ячейке совпадает с сохраненным — это повторный холостой вызов
Если Материал = ПредыдущееЗначениеМатериала Тогда
Возврат;
КонецЕсли;
Если Материал.Выбран() = 0 Тогда
Возврат;
КонецЕсли;
// Запоминаем новое значение, чтобы отсечь повторный вызов при потере фокуса
ПредыдущееЗначениеМатериала = Материал;
// Исполняемый алгоритм
Предупреждение("Вы выбрали новый материал: " + СокрЛП(Материал.Наименование));
КонецПроцедуры
Данный подход хорош тем, что он изолирован внутри одной процедуры формулы и не требует вмешательства в другие обработчики событий формы документа.
Если вы хотите полностью исключить двойной вызов на уровне платформы и сделать код максимально чистым и управляемым, рекомендуется отказаться от прописывания формулы в свойствах самой колонки табличной части. Вместо этого мы можем использовать штатный механизм перехвата интерактивного выбора платформы.
Для реализации этого способа выполните следующие шаги:
ПриНачалеВыбораЗначения(). В ней мы отлавливаем клик по нужной колонке, отменяем стандартное открытие справочника платформой (присваивая ФлагСтандОбр = 0) и открываем форму выбора вручную.ОбработкаВыбораЗначения().Проанализируем пример реализации данного подхода:
Процедура ПриНачалеВыбораЗначения(ИдентЭлемента, ФлагСтандОбр)
Если ИдентЭлемента = "Материал" Тогда
// Отключаем стандартный вызов справочника платформой
ФлагСтандОбр = 0;
// Открываем форму выбора справочника программно, передавая контекст формы
ОткрытьФорму("Справочник.Материалы", Контекст);
КонецЕсли;
КонецПроцедуры
Процедура ОбработкаВыбораЗначения(ВыбЗнач, ИдентЭлемента, ФлагСтандОбр)
Если ИдентЭлемента = "Материал" Тогда
// Проверяем, что пользователь действительно выбрал элемент, а не закрыл форму выбора
Если ПолучитьПустоеЗначение("Справочник.Материалы") <> ВыбЗнач Тогда
// Здесь пишем всю необходимую логику, включая вопросы пользователю.
// Этот код гарантированно выполнится строго один раз!
Если Вопрос("Вы подтверждаете выбор материала " + СокрЛП(ВыбЗнач.Наименование) + "?", "Да+Нет") = "Да" Тогда
// Записываем выбранное значение непосредственно в реквизит текущей строки ТЧ
Материал = ВыбЗнач;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Преимущества данного метода: Полное отсутствие паразитных вызовов формул, жесткий контроль над процессом выбора значения, улучшение отзывчивости интерфейса и стабильности работы приложения при потере фокуса элементами табличной части.
В платформе 1С 7.7 существует встроенная функция управления поведением расчета формул на форме — ВыполнятьФормулуТолькоПриИзменении(). Если вызвать этот метод при открытии формы, платформа теоретически должна блокировать вызовы формул колонок, если значение в ячейке фактически не поменялось.
Для включения этого режима достаточно добавить строку кода в процедуру инициализации формы:
Процедура ПриОткрытии()
// Устанавливаем режим выполнения формул только при реальном изменении данных
ВыполнятьФормулуТолькоПриИзменении(1);
КонецПроцедуры
Важное примечание: Несмотря на простоту, этот метод имеет свои недостатки. На практике в сложных комплексных конфигурациях данный флаг может вести себя нестабильно. Если внутри процедуры формулы выполняются программные модификации других реквизитов текущей строки с последующим вызовом Форма.Обновить(), платформа может проигнорировать этот режим и все равно выполнить формулу повторно. Поэтому используйте этот метод с осторожностью и тщательно тестируйте логику работы формы во всех режимах ввода.
Мы рассмотрели четыре различных способа решения проблемы двойного вызова формул в табличной части 1С 7.7. Подводя итог, можно дать следующие рекомендации по их применению:
ПриНачалеВыбораЗначения). Это наиболее профессиональный, надежный и прозрачный способ управления событиями интерфейса в 1С 7.7, который убережет вас от любых побочных эффектов работы старого программного движка.