В процессе разработки интерфейсов на платформе 1С:Предприятие (особенно в обычных формах / толстом клиенте) часто возникает задача визуального выделения строк в зависимости от определенных условий. Например, требуется выделить срочные заявки красным цветом, а выполненные — вернуть к стандартному белому фону. На первый взгляд задача кажется тривиальной, но при её решении начинающие разработчики часто сталкиваются с двумя типами проблем: синтаксическими ошибками в условных конструкциях и критическим падением производительности интерфейса при прокрутке списков. В этой статье мы подробно разберем, как правильно реализовать раскраску строк, исправить типичные ошибки и оптимизировать код для мгновенной работы динамического списка.
При написании логики раскраски в модуле формы разработчики часто вкладывают одно условие в другое или добавляют новые проверки в уже существующие цепочки условий. Рассмотрим типичную ситуацию, когда код перестает компилироваться и платформа выдает ошибку:
{Документ.ЗаявкиИТ.Форма.ФормаСписка.Форма(28,1)}: Ожидается ключевое слово 'КонецПроцедуры' ('EndProcedure')
Выясним причину этой ошибки. Она возникает из-за нарушения парности условных операторов Если — Тогда — ИначеЕсли — КонецЕсли. Если вы случайно пропустили закрывающий оператор или, наоборот, добавили лишний КонецЕсли внутри процедуры, компилятор 1С теряет структуру кода и считает, что процедура завершилась некорректно.
Проанализируем ситуацию на примере. Чтобы избежать подобных проблем, настоятельно рекомендуется использовать встроенный инструмент форматирования кода. Для этого выполните следующие действия в конфигураторе:
Платформа автоматически расставит отступы (табуляцию) в соответствии с вложенностью логических блоков. Если после форматирования вы увидите, что ключевое слово КонецПроцедуры сдвинулось вправо или операторы закрытия условий выстроились лесенкой, значит, вы пропустили или продублировали один из операторов КонецЕсли. Выравнивание кода по стандартам разработки сразу покажет, в каком месте нарушена логика программы.
Другая распространенная ошибка — попытка напрямую сравнить ссылку на элемент справочника со строковым значением. Рассмотрим неработающий пример кода, который пытался использовать автор темы:
Если ДанныеСтроки.Ссылка.СтатусЗаявки = "Выполнено" Тогда
ОформлениеСтроки.ЦветФона = Новый Цвет(255, 255, 255);
КонецЕсли;
Если реквизит СтатусЗаявки имеет тип СправочникСсылка.СтатусыЗаявок, то прямое сравнение со строкой "Выполнено" всегда будет возвращать значение Ложь. В результате цвет строк меняться не будет. Чтобы решить эту проблему «в лоб», разработчики часто используют поиск элемента в базе данных по наименованию прямо внутри отрисовки строки:
Если ДанныеСтроки.Ссылка.СтатусЗаявки = Справочники.СтатусыЗаявок.НайтиПоНаименованию("Выполнено") Тогда
ОформлениеСтроки.ЦветФона = Новый Цвет(255, 255, 255);
КонецЕсли;
Хотя этот код синтаксически корректен и решит задачу перекраски строк, он содержит в себе огромную угрозу для производительности всей системы. Разберем по шагам, почему так делать категорически нельзя.
Каждый раз, когда вы пишете конструкцию вида ДанныеСтроки.Ссылка.СтатусЗаявки, платформа выполняет так называемое неявное разыменование ссылки. Для получения одного реквизита система вынуждена выполнить полноценный SQL-запрос к базе данных и прочитать весь объект документа целиком. Если на экране пользователя отображается 30 строк, система выполнит 30 независимых запросов к СУБД для отрисовки одного экрана.
Ситуация усугубляется использованием метода НайтиПоНаименованию("Выполнено"). Этот метод инициирует еще один полноценный поиск в базе данных по строковому полю. Если этот код помещен в событие ПриВыводеСтроки, то при любой попытке пользователя прокрутить список, изменить размер формы или просто пошевелить мышкой над табличным полем, 1С будет непрерывно отправлять сотни тяжелых запросов к серверу СУБД. Это гарантированно приведет к зависанию интерфейса пользователя и создаст избыточную нагрузку на сервер.
Для создания быстрого и отзывчивого интерфейса в обычных формах необходимо соблюдать три золотых правила оптимизации списков:
НайтиПоНаименованию, НайтиПоКоду) внутри отрисовки строк. Все необходимые значения должны кэшироваться заранее.ПриВыводеСтроки использовать пакетное событие ПриПолученииДанных.Разница между ними принципиальна. Событие ПриВыводеСтроки срабатывает отдельно для каждой отрисовываемой строки. Событие ПриПолученииДанных вызывается один раз для всей совокупности строк, отображаемых на экране в данный момент. Это позволяет нам собрать ссылки всех видимых строк, получить их статусы одним быстрым запросом к базе данных и мгновенно раскрасить интерфейс в оперативной памяти компьютера.
Рассмотрим подробный пример правильной и оптимизированной реализации этой задачи:
// Объявляем переменную в начале модуля формы для кэширования статуса
Перем мСтатусВыполнено;
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
// Кэшируем нужный нам статус один раз при открытии формы,
// чтобы полностью исключить метод НайтиПоНаименованию() из процесса отрисовки
мСтатусВыполнено = Справочники.СтатусыЗаявок.НайтиПоНаименованию("Выполнено");
КонецПроцедуры
Процедура СписокПриПолученииДанных(Элемент, ОформленияСтрок)
// Шаг 1. Собираем массив ссылок на документы, которые выводятся на экран в данный момент
МассивСсылок = Новый Массив;
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
Если ОформлениеСтроки.ДанныеСтроки <> Неопределено Тогда
МассивСсылок.Добавить(ОформлениеСтроки.ДанныеСтроки.Ссылка);
КонецЕсли;
КонецЦикла;
// Если на экране нет строк (например, список пуст), прерываем выполнение
Если МассивСсылок.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Шаг 2. Получаем статусы для всех видимых документов ОДНИМ запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Заявки.Ссылка КАК Ссылка,
| Заявки.СтатусЗаявки КАК СтатусЗаявки
|ИЗ
| Документ.ЗаявкиИТ КАК Заявки
|ГДЕ
| Заявки.Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
// Шаг 3. Загружаем результаты запроса в Соответствие (быструю хэш-таблицу в памяти)
СоответствиеСтатусов = Новый Соответствие;
Для Каждого СтрокаТД Из ТаблицаДанных Цикл
СоответствиеСтатусов.Вставить(СтрокаТД.Ссылка, СтрокаТД.СтатусЗаявки);
КонецЦикла;
// Шаг 4. Обходим оформление строк и раскрашиваем их без обращений к БД
ЦветБелый = Новый Цвет(255, 255, 255);
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
Если ОформлениеСтроки.ДанныеСтроки = Неопределено Тогда
Продолжить;
КонецЕсли;
СсылкаНаДокумент = ОформлениеСтроки.ДанныеСтроки.Ссылка;
СтатусДокумента = СоответствиеСтатусов.Получить(СсылкаНаДокумент);
// Сравниваем полученный статус с закэшированным значением
Если СтатусДокумента = мСтатусВыполнено Тогда
ОформлениеСтроки.ЦветФона = ЦветБелый;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Благодаря такому подходу, прокрутка списка документов любого объема (даже если в вашей базе данных сотни тысяч записей) будет происходить мгновенно, без задержек и подвисаний интерфейса.
Использование справочников для хранения фиксированных системных состояний (таких как Новая, В работе, Выполнена, Отклонена) является грубой архитектурной ошибкой при проектировании структуры метаданных конфигурации 1С. Разберем ключевые причины, почему для этих целей всегда следует использовать объект метаданных Перечисление:
Перечисления.СтатусыЗаявок.Выполнено.Если вы планируете переход или уже разрабатываете решения на управляемых формах (платформа 1С 8.2 / 8.3), то ручное написание кода для раскраски строк в событиях отображения больше не требуется. Платформа предоставляет для этого готовый декларативный механизм — Условное оформление динамического списка.
В управляемом интерфейсе разработчику достаточно открыть свойства динамического списка, перейти на вкладку Условное оформление и добавить новое правило:
СтатусЗаявки Равно СсылкаНаСтатусВыполнено (или выбрать значение перечисления напрямую).Платформа автоматически добавит необходимые поля в SQL-запрос динамического списка и выполнит всю работу по отрисовке интерфейса на уровне ядра платформы с максимальной производительностью.