Как правильно реализовать чередование цветов строк табличного поля по дням в 1С

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов в обычных формах 1С часто возникает задача визуального разделения данных. Стандартное свойство элемента управления ЧередованиеЦветовСтрок окрашивает каждую четную или нечетную строку независимо от их смыслового содержания. Если же требуется объединить строки в визуальные блоки — например, выделить одним цветом все занятия одного дня, а следующим цветом — занятия другого дня, разработчики сталкиваются со специфическим поведением платформы: при вертикальной прокрутке таблицы цвета начинают самопроизвольно переключаться и сбиваться.

Разберем, почему возникает эта ошибка в обработчиках событий табличного поля и какими способами можно реализовать стабильное оформление строк.

В чем причина сбоя цветов при прокрутке таблицы

Проанализируем механику работы событий ПриПолученииДанных и ПриВыводеСтроки. Платформа 1С для оптимизации быстродействия никогда не передает в эти процедуры таблицу целиком, если количество строк превышает видимую область экрана. В параметр ОформленияСтрок попадают только те записи, которые отображаются на мониторе в текущий момент времени (плюс небольшой буферный запас для плавной прокрутки).

Посмотрим на типичную ошибку при попытке чередования цветов внутри события ПриПолученииДанных:


Процедура ЗанятияПриПолученииДанных(Элемент, ОформленияСтрок)
    Если ОформленияСтрок.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    МЦ = Новый Массив;
    МЦ.Добавить(WebЦвета.Пшеничный);
    МЦ.Добавить(WebЦвета.Белый);

    ТекущийДень = НачалоДня(ОформленияСтрок[0].ДанныеСтроки.ДатаНачалаЗанятия);
    ИндексЦвета = 0;

    Для Каждого офСтроки Из ОформленияСтрок Цикл
        Если ТекущийДень <> НачалоДня(офСтроки.ДанныеСтроки.ДатаНачалаЗанятия) Тогда
            ИндексЦвета = (ИндексЦвета + 1) % 2;
            ТекущийДень = НачалоДня(офСтроки.ДанныеСтроки.ДатаНачалаЗанятия);
        КонецЕсли;
        
        офСтроки.ЦветФона = МЦ[ИндексЦвета];
    КонецЦикла;
КонецПроцедуры

Выясним причину некорректной работы приведенного алгоритма. Как только пользователь сдвигает полосу прокрутки хотя бы на одну строку вниз, состав коллекции ОформленияСтрок полностью меняется. Нулевой элемент ОформленияСтрок[0] теперь указывает на совершенно другую запись. Счетчик ИндексЦвета снова сбрасывается в исходное значение 0 относительно новой верхней строки, а не относительно фактического начала документа. В итоге строки одного и того же дня начинают менять цвет при малейшем движении ползунка.

Главное правило оформления табличных полей: цвет строки должен рассчитываться детерминированно. Он не должен зависеть от порядка прорисовки или соседних элементов в текущей порции видимых строк.

Способ 1. Предварительный расчет и кэширование соответствия цветов (рекомендуемый)

Рассмотрим наиболее производительное и универсальное решение, которое не требует изменения структуры метаданных конфигурации. Идея заключается в том, чтобы один раз обойти всю коллекцию строк документа, определить цвет для каждой уникальной даты и сохранить результат в переменной модуля формы типа Соответствие.

Разберем реализацию по шагам:

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

Такой подход гарантирует минимальную нагрузку на платформу: доступ к элементам объекта Соответствие осуществляется за постоянное время O(1), а при скроллинге не происходит лишних итераций по строкам.

Способ 2. Использование служебного реквизита строки

Если конфигурация доступна для доработки структуры метаданных, задачу можно решить еще проще:

  1. Добавим в табличную часть Занятия служебный реквизит ИндексЦвета (тип: Число, длина 1, неотрицательное).
  2. При записи документа или при изменении дат перезаполняем этот индекс, присваивая строкам одного дня значение 0, а строкам следующего — значение 1.
  3. В событии ПриПолученииДанных или ПриВыводеСтроки напрямую назначаем оформление, используя значение этого реквизита:
    
    Процедура ЗанятияПриПолученииДанных(Элемент, ОформленияСтрок)
        Для Каждого офСтроки Из ОформленияСтрок Цикл
            Если офСтроки.ДанныеСтроки.ИндексЦвета = 1 Тогда
                офСтроки.ЦветФона = WebЦвета.Пшеничный;
            Иначе
                офСтроки.ЦветФона = WebЦвета.Белый;
            КонецЕсли;
        КонецЦикла;
    КонецПроцедуры
    

Почему не подходит простая проверка четности дня

На профильных форумах нередко предлагают определять цвет строки по формуле четности дня: ДеньГода(Дата) % 2. Однако на практике этот метод приводит к ошибкам. Если занятия в расписании проводятся только по фиксированным дням недели (например, вторник, четверг и суббота), то остаток от деления порядкового номера дня на 2 может совпадать для всех занятий подряд на протяжении недели. В результате визуальное разделение между соседними учебными днями полностью пропадет.

Особенности реализации в Управляемых формах

Стоит отметить, что в режиме управляемого приложения платформы 1С:Предприятие события ПриПолученииДанных и ПриВыводеСтроки отсутствуют вовсе. В управляемом интерфейсе раскрашивание реализуется через коллекцию УсловноеОформление формы. Поскольку механизм условного оформления умеет анализировать только поля текущей строки и не имеет доступа к соседним строкам, единственным надежным вариантом для управляемых форм остается Способ 2 (расчет признака четности группы в реквизит строки и привязка цвета через стандартное условное оформление).

← На главную