Как проверить, изменились ли данные в строке табличной части на обычных формах 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Разработка интерфейсов на обычных формах (ОФ) в 1С часто преподносит интересные задачи, требующие глубокого понимания жизненного цикла событий элементов управления. Типичный сценарий: на форме обработки или документа расположена табличная часть с огромным количеством колонок (например, 30, 50 или даже больше). Пользователь дважды кликает по строке, входит в режим редактирования, что-то просматривает, возможно, случайно меняет значение, затем возвращает его назад или просто нажимает клавишу Enter.

Перед разработчиком встает задача: как с максимальной точностью и минимальными затратами производительности определить, действительно ли строка была изменена? Нам нужно отсечь ситуации, когда пользователь просто зашел в режим редактирования и вышел из него без изменения данных, а также ситуации, когда значение было изменено на точно такое же.

В этой статье мы подробно разберем, почему стандартные события ПриИзменении не всегда подходят для этой задачи, проанализируем альтернативные варианты (например, хэширование) и реализуем самый надежный, быстрый и элегантный паттерн — динамическое кэширование значений строки с использованием платформенных механизмов 1С.

Почему стандартные методы не дают 100% надежности?

Давайте проанализируем стандартные подходы, которые первыми приходят в голову, и выясним их слабые стороны:

  1. Использование события ПриИзменении для каждой колонки. На первый взгляд, это логично. Но представьте, что в вашей табличной части 40 колонок. Создавать 40 обработчиков событий или привязывать их к одному общему — это огромная рутинная работа. Более того, событие ПриИзменении на поле ввода срабатывает в тот момент, когда пользователь интерактивно выбирает значение. Если пользователь выбрал в выпадающем списке точно то же значение, которое там уже было, событие все равно сгенерируется, хотя фактического изменения данных не произошло.
  2. Использование события ПриОкончанииРедактирования табличного поля. Это событие действительно сигнализирует о завершении процесса редактирования строки. У него есть замечательный параметр ОтменаРедактирования, который принимает значение Истина, если пользователь нажал клавишу Esc. Однако, если пользователь нажал Enter или кликнул мышкой на другую строку, не изменив ни одной цифры, параметр ОтменаРедактирования будет равен Ложь. Платформа сама по себе не сравнивает состояние «было» и «стало» для всей строки.
  3. Сравнение с данными базы данных. Если мы редактируем строку документа или справочника, который уже записан в базе, мы могли бы получить объект из базы данных по ссылке, найти строку по индексу и сравнить ее с текущей строкой на форме. Но что делать, если объект еще не записан? Или если наша таблица вообще принадлежит внешней обработке и не связана ни с какими объектами метаданных? Этот способ не универсален.

С учетом этих ограничений лучшим решением является сохранение исходного состояния строки непосредственно перед началом ее редактирования и последующее сравнение с новым состоянием при завершении редактирования.

Универсальный алгоритм динамического кэширования строки

Мы рассмотрим изящный способ, который работает на уровне платформы 1С v8 и не требует прописывания имен колонок вручную. Этот алгоритм динамически адаптируется под любой состав колонок табличной части.

Идея заключается в следующем:

  1. В момент начала редактирования строки мы динамически считываем все колонки табличного поля, создаем структуру, где ключами выступают имена колонок, и копируем туда текущие значения строки с помощью быстрого платформенного метода ЗаполнитьЗначенияСвойств.
  2. В момент окончания редактирования мы сравниваем свойства текущей строки со свойствами сохраненного кэша. Если найдено хотя бы одно несовпадение — строка изменена!

Давайте разберем по шагам реализацию этого механизма.

Шаг 1. Объявление переменной для хранения кэша

В самом верху модуля нашей формы, в разделе объявлений переменных, добавим переменную, которая будет хранить кэш-структуру на время редактирования строки:

Перем КэшСтрокиДоИзменения;

Эта переменная будет видна всем процедурам модуля формы, что позволит нам легко передавать данные между событиями начала и окончания редактирования.

Шаг 2. Захват данных в событии «ПриНачалеРедактирования»

Создадим обработчик события ПриНачалеРедактирования для нашего табличного поля. В этом обработчике мы динамически соберем имена всех колонок, исключая служебные (например, колонки с кнопками или декорациями, у которых нет свойства Данные), и запишем текущие значения строки в структуру.


Процедура ТабличноеПолеПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
    
    // Если создается абсолютно новая строка, кэшировать нечего — она гарантированно изменит состав таблицы
    Если НоваяСтрока Тогда
        КэшСтрокиДоИзменения = Неопределено;
        Возврат;
    КонецЕсли;
    
    // Динамически собираем список колонок, привязанных к данным
    СписокКолонок = "";
    Для Каждого Колонка Из Элемент.Колонки Цикл
        Если ЗначениеЗаполнено(Колонка.Данные) Тогда
            СписокКолонок = СписокКолонок + ?(СписокКолонок = "", "", ",") + Колонка.Данные;
        КонецЕсли;
    КонецЦикла;
    
    // Создаем структуру, ключами которой будут имена колонок
    КэшСтрокиДоИзменения = Новый Структура(СписокКолонок);
    
    // Платформенный метод мгновенно переносит значения из строки табличной части в структуру
    ЗаполнитьЗначенияСвойств(КэшСтрокиДоИзменения, Элемент.ТекущиеДанные);
    
КонецПроцедуры

Обратите внимание на использование метода ЗаполнитьЗначенияСвойств. Он работает на уровне ядра платформы 1С и выполняет копирование значений свойств чрезвычайно быстро. Даже если у вас в таблице огромное количество колонок, этот код выполнится незаметно для пользователя.

Шаг 3. Сравнение и выявление изменений в «ПриОкончанииРедактирования»

Теперь перейдем к обработчику события ПриОкончанииРедактирования. Здесь мы проверим, не было ли отмены редактирования, и если сессия редактирования завершилась штатно — пошагово сравним текущее состояние строки с кэшем.


Процедура ТабличноеПолеПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
    
    // Если пользователь нажал ESC или отменил редактирование программно, прерываем проверку
    Если ОтменаРедактирования Тогда
        Возврат;
    КонецЕсли;
    
    // Если это новая строка, фиксируем изменение без детального сравнения
    Если НоваяСтрока Тогда
        ОбработатьИзменениеСтроки(Элемент.ТекущиеДанные);
        Возврат;
    КонецЕсли;
    
    // На всякий случай проверяем наличие кэша
    Если КэшСтрокиДоИзменения = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    ТекущиеДанные = Элемент.ТекущиеДанные;
    СтрокаИзменена = Ложь;
    
    // Обходим ключи кэш-структуры и сравниваем значения
    Для Каждого КлючИЗначение Из КэшСтрокиДоИзменения Цикл
        ИмяРеквизита = КлючИЗначение.Ключ;
        
        // Сравниваем значение в текущей строке со старым значением в кэше
        Если ТекущиеДанные[ИмяРеквизита] <> КлючИЗначение.Значение Тогда
            СтрокаИзменена = Истина;
            Прервать; // Достаточно одного отличия, чтобы прервать проверку
        КонецЕсли;
    КонецЦикла;
    
    // Если изменения зафиксированы, выполняем необходимую логику
    Если СтрокаИзменена Тогда
        ОбработатьИзменениеСтроки(ТекущиеДанные);
    КонецЕсли;
    
    // Очищаем кэш после завершения операции
    КэшСтрокиДоИзменения = Неопределено;
    
КонецПроцедуры

Метод ОбработатьИзменениеСтроки — это ваша произвольная процедура, в которой вы выполняете нужные действия (например, пересчет итогов по строке, установка флага модифицированности формы, логирование изменений или валидация данных).

Анализ альтернативы: Стоит ли использовать хэширование?

На форумах часто проскакивает идея использования хэш-функций для решения подобных задач. Идея заманчива: получить хэш всей строки до изменения, получить хэш после изменения и сравнить два числа (или строки).

Выясним, почему для 1С этот подход избыточен:

Таким образом, динамическое кэширование через структуру остается неоспоримым лидером по производительности, надежности и простоте сопровождения кода.

Достоинства предложенного решения

Давайте резюмируем, почему данный подход является профессиональным стандартом разработки в среде 1С:

  1. Полная независимость от структуры таблицы. Если завтра аналитики потребуют добавить в табличную часть еще 10 колонок, вам не придется менять ни одной строчки в коде сравнения. Алгоритм автоматически определит новые колонки и включит их в проверку.
  2. Оптимальное использование ресурсов. Мы не храним кэш для всей таблицы. В памяти находится состояние только одной строки — той, которую пользователь редактирует в данный момент.
  3. Отсутствие ложных срабатываний. Проверка работает по факту изменения значений, игнорируя пустые клики или случайный выбор того же самого значения из списка.

Используйте этот шаблон в своих обработках и документах на обычных формах, чтобы сделать интерфейс приложения отзывчивым, быстрым и надежным!

← На главную