При разработке интерфейсов в 1С часто возникает задача визуального отображения занятости специалистов, бронирования оборудования или графиков отпусков с помощью элемента формы Поле календаря. Для раскраски дат обычно используется обработчик события ПриВыводеПериода. Однако разработчики сталкиваются с серьезным падением производительности интерфейса или ошибками проектирования, когда пытаются искать нужные даты прямо в таблицах формы при каждой отрисовке периода. Разберем причины этой проблемы и выясним, как правильно построить архитектуру быстрого поиска элементов.
Проанализируем механику работы события ПриВыводеПериода. Данное событие платформы вызывается порционно (помесячно) для отображаемого в календаре диапазона. Внутри каждого периода содержится коллекция ОформлениеПериода.Даты, насчитывающая от 28 до 31 элемента. В сумме за год обработчик вызывается многократно, выполняя сотни итераций.
Если внутри каждого шага выполнять поиск по объекту ДанныеФормыКоллекция (табличная часть или таблица формы), мы сталкиваемся со следующими препятствиями:
ДанныеФормыКоллекция нет встроенного индексного поиска. Метод НайтиСтроки или последовательный цикл перебирают все строки заново. При количестве строк $N$ и числе отображаемых дат $M$ сложность алгоритма возрастает до $O(N \times M)$, что моментально вызывает подтормаживание пользовательского интерфейса при прокрутке.ТаблицаЗначений с индексами приводит к ошибке, так как эта коллекция недоступна в контексте тонкого клиента.ПриВыводеПериода недопустим. Контекстный вызов сервера при перерисовке приводит к лавинообразному зацикливанию интерфейса и зависанию программы.ОформлениеПериода.Даты каждая дата передается строго с нулевым временем (00:00:00). Если исходные данные содержат время, отличное от начала дня, прямое сравнение дат будет возвращать Ложь.Наиболее производительный и архитектурно правильный способ — выполнить предварительную подготовку данных и сохранить их в клиентской переменной модуля формы с использованием объекта Соответствие. Поиск по ключу в Соответствие выполняется за константное время $O(1)$, что обеспечивает мгновенную отрисовку календаря.
Разберем пошагово реализацию этого механизма.
&НаКлиенте
Перем мКэшЗанятыхДат;
НачалоДня().
&НаСервере
Функция ПолучитьСоответствиеЗанятыхДатСервер()
Кэш = Новый Соответствие;
ЦветЗанято = Новый Цвет(255, 200, 200); // Светло-красный цвет фона
Для Каждого СтрТЗ Из Объект.ТаблицаЗанятости Цикл
// Нормализуем дату к началу дня для точного сопоставления
ДатаКлюч = НачалоДня(СтрТЗ.ДатаВПериоде);
Кэш.Вставить(ДатаКлюч, ЦветЗанято);
КонецЦикла;
Возврат Кэш;
КонецФункции
&НаКлиенте
Процедура ОбновитьКэшКалендаря()
мКэшЗанятыхДат = ПолучитьСоответствиеЗанятыхДатСервер();
Элементы.Календарь.Обновить();
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ОбновитьКэшКалендаря();
КонецПроцедуры
&НаКлиенте
Процедура КалендарьПриВыводеПериода(Элемент, ОформлениеПериода)
Если мКэшЗанятыхДат = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого ТекОформление Из ОформлениеПериода.Даты Цикл
ЦветДня = мКэшЗанятыхДат[ТекОформление.Дата];
Если ЦветДня <> Неопределено Тогда
ТекОформление.ЦветФона = ЦветДня;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рассмотрим альтернативные подходы, которые часто обсуждаются при решении подобных задач:
ВыделенныеДаты элемента формы: У элемента ПолеКалендаря доступно свойство ВыделенныеДаты, куда можно передать массив дат. Однако этот способ имеет жесткое ограничение: выделенные даты подсвечиваются только стандартным цветом пользовательского выделения платформы. Он не позволяет задавать произвольный цвет текста, фона или выводить индивидуальные подсказки для разных мастеров и статусов.ПолеКалендаря к элементу Поле планировщика (реквизит формы с типом Планировщик). Этот объект платформы изначально спроектирован для вывода интервалов, расписаний и ресурсов, автоматически обрабатывает пересечения периодов и избавляет разработчика от необходимости вручную оптимизировать низкоуровневые события отрисовки ячеек.