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