При разработке и доработке конфигураций на обычных формах (например, «Управление торговлей 10.3», «Управление производственным предприятием 1.3» или «Комплексная автоматизация 1.1») разработчики часто сталкиваются с задачей оформления списков. Нередко возникает ситуация: на форме размещено табличное поле с типом данных динамического списка, таким как РегистрСведенийСписок, и для удобства пользователя часть колонок скрыта (установлено свойство Видимость = Ложь). Однако в обработчике ПриВыводеСтроки требуется проверить значение ресурса или измерения из этой невидимой колонки, чтобы изменить цвет текста, фон ячейки или вывести пиктограмму. При попытке прочитать значение разработчик обнаруживает, что скрытое поле оказывается пустым или вовсе отсутствует в выборке.
Разберем детально механизм работы динамических списков платформы 1С, выясним причину такого поведения и рассмотрим правильные способы решения этой проблемы.
Проанализируем архитектуру динамических списков в обычных формах (это касается объектов РегистрСведенийСписок, СправочникСписок, ДокументСписок и других подобных типов). Платформа 1С оптимизирует запросы к СУБД для минимизации нагрузки на сеть и сервер. Формируя SQL-запрос к базе данных, платформа включает в итоговую выборку не все существующие поля таблицы, а только те, которые действительно необходимы для отображения или управления интерфейсом:
Видимость = Истина.Отбор).Порядок).Если колонка скрыта разработчиком в конфигураторе, отключена программно через Колонка.Видимость = Ложь или выключена пользователем через стандартное окно «Настройка списка», платформа считает это поле избыточным и полностью вырезает его из текста запроса. В результате при отрисовке строк значение этого реквизита или ресурса физически не считывается из базы данных.
Посмотрим на типичную ошибку начинающих разработчиков. Часто при попытке получить значение ячейки в обработчике отрисовки обращаются к свойству элемента формы ЭлементыФормы.ТабличноеПоле.ТекущиеДанные или ТекущаяСтрока. Делать это категорически нельзя по следующим причинам:
ТекущиеДанные возвращает данные исключительно той строки, на которой в настоящий момент установлен курсор (фокус ввода).ПриВыводеСтроки вызывается циклически для каждой видимой на экране строки при первоначальной отрисовке, прокрутке или обновлении списка.ТекущиеДанные внутри ПриВыводеСтроки, оформление абсолютно всех отображаемых строк списка будет ошибочно рассчитываться по значениям одной-единственной активной строки.Правило: данные конкретной отрисовываемой строки необходимо получать исключительно из параметров самого обработчика события — через переданный параметр ДанныеСтроки либо через свойство ОформлениеСтроки.ДанныеСтроки.
Рассмотрим структуру элементов формы. Важно различать две разные коллекции колонок:
КолонкиТабличногоПоля), доступная через ЭлементыФормы.ИмяТабличногоПоля.Колонки. Именно здесь настраиваются заголовок, ширина и свойство Видимость.КолонкиСписка), доступная напрямую через реквизит формы, например РегистрСведенийСписок.Колонки.Каждый элемент коллекции КолонкиСписка обладает важнейшим свойством — АвтоУдаление. По умолчанию оно имеет значение Истина, что разрешает платформе удалять поле из запроса, если колонка скрыта. Если установить АвтоУдаление = Ложь, платформа будет принудительно запрашивать данные этого поля из СУБД независимо от того, видит его пользователь на экране или нет.
Разберем по шагам реализацию этого механизма в коде модуля формы. Программную настройку колонок списка необходимо выполнять на этапе инициализации формы, в процедуре-обработчике ПередОткрытием или ПриОткрытии:
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
// Имя поля регистра, значение которого нам потребуется в коде
ИмяПоля = "ЗначениеНастройки";
// Ищем колонку в коллекции колонок динамического списка
Колонка = РегистрСведенийСписок.Колонки.Найти(ИмяПоля);
Если Колонка <> Неопределено Тогда
// Запрещаем платформе исключать колонку из запроса при невидимости
Колонка.АвтоУдаление = Ложь;
Иначе
// Если колонки нет в списке, добавляем её с параметром АвтоУдаление = Ложь
РегистрСведенийСписок.Колонки.Добавить(ИмяПоля, Ложь);
КонецЕсли;
КонецПроцедуры
После отключения автоудаления мы можем скрыть визуальную колонку табличного поля от пользователя, не опасаясь потери данных:
// Скрываем визуальное отображение от пользователя
ЭлементыФормы.ТабличноеПолеСписок.Колонки.ЗначениеНастройки.Видимость = Ложь;
Теперь в обработчике события ПриВыводеСтроки скрытое поле всегда будет присутствовать в структуре ДанныеСтроки с актуальным значением из базы:
Процедура ТабличноеПолеСписокПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
Если ДанныеСтроки <> Неопределено Тогда
// Безопасно считываем скрытый ресурс или реквизит
Если ДанныеСтроки.ЗначениеНастройки = Истина Тогда
ОформлениеСтроки.ЦветТекста = ЦветаСтиля.ЦветОсобогоТекста;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Посмотрим на частую проблему: разработчики пытаются обратиться к свойству ДанныеСтроки.Ссылка или получить идентификатор записи регистра сведений, но поле возвращает пустое значение. В отличие от справочников или документов, запись регистра сведений не является ссылочным объектом. Ее однозначная идентификация выполняется с помощью объекта КлючЗаписи.
Платформа формирует КлючЗаписи на лету на основании значений всех измерений регистра (а для периодических регистров — дополнительно на основании даты периода). Если хотя бы одно измерение исключено из выборки (скрыто визуально и для него не отключено АвтоУдаление), платформа не имеет полного набора данных и не может построить КлючЗаписи. Чтобы КлючЗаписи формировался корректно, убедитесь, что для каждого измерения регистра отключено автоудаление из запроса.
Проанализируем ситуацию, когда данных динамического списка всё же недостаточно и требуется получить дополнительную связанную информацию из информационной базы. Категорически избегайте выполнения запросов к базе данных или вызовов методов чтения (например, РегистрыСведений.Имя.Получить(...)) внутри обработчика ПриВыводеСтроки. Это приводит к возникновению проблемы «N+1 запросов»: при каждом скроллинге списка интерфейс будет «замирать», выполняя десятки отдельных запросов к СУБД для каждой строки таблицы.
Для таких задач следует переходить на событие списка ПриПолученииДанных. В этом обработчике платформа передает массив оформлений сразу для всей пачки видимых на экране строк. Это позволяет собрать массив ключей или ссылок, выполнить один пакетный запрос к базе данных и быстро расставить значения по коллекции ОформленияСтрок, сохраняя максимальную отзывчивость интерфейса системы.