Приветствуем вас, уважаемые разработчики! При переходе с управляемых форм на обычные формы многие часто сталкиваются с особенностями работы старых интерфейсных механизмов 1С. Одной из классических задач является визуальное выделение данных в табличном поле — например, при импорте накладных из Excel нам требуется сопоставить номенклатуру по артикулу или штрихкоду и красиво подсветить строки цветом в зависимости от результатов поиска. В этой статье мы подробно разберем, как реализовать такую подсветку максимально эффективно, не перегружая систему лишними запросами и избегая типичных ошибок, приводящих к «зависанию» интерфейса.
Начинающие разработчики часто пытаются выполнить запросы к базе данных непосредственно внутри событий отрисовки интерфейса, таких как ПриВыводеСтроки. Это критическая ошибка проектирования. Давайте проанализируем, почему так происходит.
Событие ПриВыводеСтроки вызывается платформой 1С непрерывно: при каждом скроллинге таблицы, изменении размеров окна, активации любой строки и даже при простом движении мыши над табличным полем. Если внутри этого события написать запрос к базе данных (например, поиск номенклатуры по артикулу), то этот запрос будет выполняться сотни раз в секунду. В результате интерфейс вашей обработки начнет жутко тормозить, а пользователи будут жаловаться на зависание программы при любой попытке прокрутить таблицу.
Для решения этой задачи существует два правильных архитектурных подхода. Рассмотрим каждый из них во всех подробностях.
Этот подход базируется на фундаментальном правиле разработки: разделяйте логику получения данных и логику их визуального отображения. Вместо того чтобы вычислять статус строки «на лету» во время отрисовки, мы вычислим его один раз при нажатии кнопки поиска и запишем результат в скрытую служебную колонку табличной части.
Разберем по шагам реализацию этого способа:
Товары) новую реквизит-колонку с именем СпособПоиска (тип данных — Строка или Число). На форме саму колонку можно сделать невидимой для пользователя, сняв флажок «Видимость» в ее свойствах."Артикул", если по штрихкоду — "Штрихкод", а если сопоставить не удалось — "НеНайдено".Посмотрим на практический пример реализации. Сначала напишем процедуру обработки нажатия кнопки, которая выполняет поиск номенклатуры:
Процедура ПоискПоАртикулуИШтрихкоду(Кнопка)
Для Каждого СтрокаТ Из Товары Цикл
// Инициализируем статус по умолчанию
СтрокаТ.СпособПоиска = "НеНайдено";
// 1. Поиск по артикулу
Если ЗначениеЗаполнено(СтрокаТ.АртикулПос) Тогда
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ВыбАртикул", СтрокаТ.АртикулПос);
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Артикул = &ВыбАртикул";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
СтрокаТ.Номенклатура = Выборка.Номенклатура;
СтрокаТ.СпособПоиска = "Артикул";
Продолжить; // Переходим к следующей строке, так как товар найден
КонецЕсли;
КонецЕсли;
// 2. Поиск по штрихкоду (если по артикулу не нашли)
Если ЗначениеЗаполнено(СтрокаТ.ШтрихкодПос) Тогда
// Здесь может быть ваш запрос поиска по регистру сведений штрихкодов
// ...
// СтрокаТ.СпособПоиска = "Штрихкод";
КонецЕсли;
КонецЦикла;
// Очень важный шаг: принудительно обновляем строки табличного поля на форме,
// чтобы цвета применились мгновенно без ручного скроллинга!
ЭлементыФормы.Товары.ОбновитьСтроки();
КонецПроцедуры
Теперь, когда наши данные подготовлены и в каждой строке записан ее статус, мы можем написать легкий и быстрый обработчик события ПриВыводеСтроки:
Процедура ТоварыПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
// Проверяем наличие данных строки, чтобы избежать ошибок при инициализации пустых областей
Если ДанныеСтроки = Неопределено Тогда
Возврат;
КонецЕсли;
// Красим ячейки в зависимости от заполненного признака
Если ДанныеСтроки.СпособПоиска = "Артикул" Тогда
// Окрашиваем конкретные ячейки: Артикул и Номенклатуру
ОформлениеСтроки.Yчейки.АртикулПос.ЦветФона = ЦветАртикул;
ОформлениеСтроки.Yчейки.Номенклатура.ЦветФона = ЦветАртикул;
ИначеЕсли ДанныеСтроки.СпособПоиска = "Штрихкод" Тогда
// Окрашиваем ячейки Штрихкод и Номенклатура
ОформлениеСтроки.Yчейки.ШтрихкодПос.ЦветФона = ЦветШтрихкод;
ОформлениеСтроки.Yчейки.Номенклатура.ЦветФона = ЦветШтрихкод;
Иначе
// Возвращаем стандартные цвета для ненайденных позиций
ОформлениеСтроки.Yчейки.АртикулПос.ЦветФона = WebЦвета.Белый;
ОформлениеСтроки.Yчейки.Номенклатура.ЦветФона = WebЦвета.Белый;
КонецЕсли;
КонецПроцедуры
Обратите внимание на использование переменных ЦветАртикул и ЦветШтрихкод в коде выше. Крайне не рекомендуется создавать новые объекты цвета внутри обработчиков рисования строк с помощью конструкции Новый Цвет(R, G, B). Постоянное создание новых объектов в оперативной памяти заставит сборщик мусора 1С работать на пределе возможностей, что вызовет заметные микрофризы при прокрутке списка.
Правильнее всего объявить эти цвета заранее на уровне модуля формы и инициализировать их один раз при открытии:
Перем ЦветАртикул, ЦветШтрихкод; // Объявляем переменные в самом верху модуля формы
Процедура ПриОткрытии()
// Инициализируем цвета один раз при создании формы
ЦветАртикул = Новый Цвет(217, 234, 211); // Нежный пастельный зеленый
ЦветШтрихкод = Новый Цвет(211, 228, 234); // Нежный пастельный синий
КонецПроцедуры
Если по каким-то причинам вы не хотите добавлять новые колонки в табличную часть (например, табличная часть жестко завязана на типовые механизмы конфигурации, которые нельзя менять), то ваш выбор — событие ПриПолученииДанных.
В отличие от ПриВыводеСтроки, которое вызывается для каждой строки отдельно, событие ПриПолученииДанных передает в процедуру коллекцию оформлений сразу для всех видимых на экране строк (обычно это от 15 до 30 штук). Это позволяет нам применить пакетную обработку запросов.
Проанализируем алгоритм работы в этом событии:
Соответствие, где ключом будет артикул, а значением — найденная ссылка на номенклатуру.Соответствия.Давайте посмотрим, как выглядит этот код на практике:
Процедура ТоварыПриПолученииДанных(Элемент, ОформленияСтрок)
// Создаем массив для сбора артикулов из видимых строк формы
МассивАртикулов = Новый Массив;
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
ДанныеСтроки = ОформлениеСтроки.ДанныеСтроки;
Если ДанныеСтроки <> Неопределено И ЗначениеЗаполнено(ДанныеСтроки.АртикулПос) Тогда
МассивАртикулов.Добавить(ДанныеСтроки.АртикулПос);
КонецЕсли;
КонецЦикла;
Если МассивАртикулов.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Выполняем один пакетный запрос по всем видимым на экране строкам
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("МассивАртикулов", МассивАртикулов);
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Артикул КАК Артикул,
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Артикул В (&МассивАртикулов)";
ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();
// Заполняем соответствие для мгновенного поиска в памяти
СоответствиеАртикулов = Новый Соответствие;
Для Каждого СтрокаРезультата Из ТаблицаРезультатов Цикл
СоответствиеАртикулов.Вставить(СтрокаРезультата.Артикул, СтрокаРезультата.Ссылка);
КонецЦикла;
// Оформляем строки на основе полученных из запроса данных
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
ДанныеСтроки = ОформлениеСтроки.ДанныеСтроки;
Если ДанныеСтроки = Неопределено 𡽷 Продолжить;
КонецЕсли;
НайденнаяНоменклатура = СоответствиеАртикулов.Получить(ДанныеСтроки.АртикулПос);
Если НайденнаяНоменклатура <> Неопределено Тогда
// Если номенклатура успешно найдена по артикулу, закрашиваем ячейки
ОформлениеСтроки.Yчейки.АртикулПос.ЦветФона = ЦветАртикул;
ОформлениеСтроки.Yчейки.Номенклатура.ЦветФона = ЦветАртикул;
Иначе
// Сбрасываем цвета оформления до стандартных
ОформлениеСтроки.Yчейки.АртикулПос.ЦветФона = WebЦвета.Белый;
ОформлениеСтроки.Yчейки.Номенклатура.ЦветФона = WebЦвета.Белый;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Внимание! Внутри события ПриПолученииДанных категорически запрещается изменять данные самой табличной части. Вы можете только менять свойства визуального представления (свойство ОформлениеСтроки.Ячейки...ЦветФона, менять шрифт, текст отображения и т.д.).
Если вы попытаетесь внутри этого события изменить реальное значение в строке, например, написать: ДанныеСтроки.Номенклатура = НайденнаяНоменклатура, система уйдет в бесконечную рекурсию. Изменение данных вызывает автоматическую перерисовку таблицы, перерисовка таблицы вновь инициирует вызов события ПриПолученииДанных, которое опять изменит данные. Платформа 1С мгновенно зависнет или аварийно завершит работу из-за переполнения стека.
Все изменения данных (заполнение найденной номенклатуры в документе) делайте строго в обработчиках кнопок, команд или при изменении конкретных ячеек пользователем!
Мы подробно изучили два мощных и правильных механизма раскраски строк табличного поля на обычных формах 1С при работе с запросами. Способ с добавлением служебной колонки-признака является более предпочтительным, так как он полностью изолирует тяжелую бизнес-логику поиска от деликатных процессов прорисовки интерфейса. Использование же события ПриПолученииДанных с пакетной выгрузкой в быстрое Соответствие незаменимо, когда структуру табличной части менять запрещено правилами поддержки конфигурации.
Используйте эти золотые правила оптимизации, следите за выделением памяти под цвета, и ваши решения всегда будут радовать пользователей высокой скоростью работы!