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