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