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