При разработке форм списков документов в 1С на обычных формах, особенно когда возникает необходимость в динамическом оформлении строк, разработчики часто сталкиваются с поведением свойства ЭлементыФормы.Список.ТекущиеДанные. Оно может неожиданно возвращать значение "Неопределено" в таких важных событиях, как ПриОткрытии или ПриПолученииДанных, даже если список визуально не пуст и содержит документы. Давайте вместе разберемся в причинах такого поведения и рассмотрим правильные подходы к решению задачи раскрашивания строк в списках документов.
Начнем с того, что проанализируем основную причину, по которой свойство ЭлементыФормы.Список.ТекущиеДанные может быть неопределено при первом открытии формы или в начале работы с ней. Ключевой момент здесь заключается в том, что ТекущиеДанные – это свойство, которое напрямую связано с клиентским интерфейсом и отображает данные той строки, которая в данный момент активна или выделена курсором в табличном поле. Это не просто данные, загруженные в список, а именно данные *текущей, интерактивно выбранной* строки.
При первоначальном открытии формы списка, несмотря на то что данные для отображения уже загружены (и мы видим их в таблице), курсор еще не установлен ни на одну конкретную строку. Система просто отрисовала список. Следовательно, никакой "текущей" строки в понимании свойства ТекущиеДанные еще нет. Это фундаментальное поведение платформы, как справедливо отмечено в сообщении 5 на форуме: "если строка не выбрана то текущиеданные возвращает неопределено. Это база".
Ситуация меняется только после того, как пользователь совершает какое-либо действие, которое приводит к активации строки. Например, прокручивает список, кликает мышью по строке или даже изменяет размер формы, что может вызвать перерисовку и автоматическую установку курсора на первую видимую строку. После таких действий, когда одна из строк становится активной, ТекущиеДанные начинают возвращать конкретную структуру с данными этой строки. В сообщении 4 также подчеркивается этот аспект: "ТекущиеДанные это не свойство объекта табличного поля на сервере, сервер условно просто хранит таблицу, а текущая выделенная строка это свойство клиентского элемента интерфейса".
Важно понимать, что событие ПриОткрытии происходит до того, как форма полностью отобразится и курсор будет установлен. А ПриПолученииДанных, хоть и вызывается для формирования отображения, также не гарантирует, что к моменту его вызова уже будет определена *текущая* строка в смысле пользовательского выбора, поскольку оно работает со *всеми* видимыми строками.
Таким образом, попытка получить ТекущиеДанные в ПриОткрытии или ПриПолученииДанных с целью оформления строк является неверным подходом, поскольку это свойство предназначено для работы с одной конкретной, выбранной пользователем строкой, а не для массового оформления элементов списка.
Задача раскрашивания строк в списке документов по определенному условию, например, в зависимости от состояния регистра сведений, является стандартной и очень распространенной. Для ее решения в 1С на обычных формах существует специально предназначенный механизм – событие ПриПолученииДанных табличного поля и его параметр ОформленияСтрок. Этот подход позволяет оформить каждую видимую строку списка индивидуально, без привязки к "текущей" строке.
ЭлементыФормы.ДокументСписок) создан обработчик события ПриПолученииДанных. Это событие вызывается платформой каждый раз, когда табличному полю требуются данные для отображения строк, например, при прокрутке, изменении порядка сортировки или фильтрации.ОформленияСтрок: Внутри обработчика ПриПолученииДанных вы получаете доступ к параметру ОформленияСтрок. Этот параметр представляет собой коллекцию объектов ОформлениеСтроки, по одному для каждой строки, которая в данный момент должна быть отображена на экране.ОформлениеСтроки в этой коллекции имеет свойство ДанныеСтроки. Именно через это свойство мы получаем доступ к самим данным, которые отображаются в данной строке списка. Для списка документов это обычно будет ссылка на документ (например, ОформлениеСтроки.ДанныеСтроки.Ссылка).ОформлениеСтроки предоставляют такие свойства, как ЦветФона, ЦветТекста, Шрифт и другие, которые позволяют изменить визуальный вид строки.Неправильный подход (и почему он не работает):
Как мы уже выяснили, попытка в ПриПолученииДанных использовать ЭлементыФормы.Список.ТекущиеДанные для раскрашивания строк является некорректной. Во-первых, оно может быть неопределено. Во-вторых, даже если бы оно было определено (например, после ручной установки курсора), это позволило бы оформить только *одну* текущую строку, а не *все* строки по условию, что является целью раскрашивания списка. В сообщениях 2 и 3 участники форума справедливо указывают на это, предлагая использовать ОформленияСтрок.
Пример базового (неоптимизированного) подхода:
Процедура ДокументСписокПриПолученииДанных(Элемент, ОформленияСтрок)
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
// Получаем ссылку на документ из текущей строки списка
СсылкаНаДокумент = ОформлениеСтроки.ДанныеСтроки.Ссылка;
// Здесь должен быть код обращения к регистру сведений
// Это пример, требующий оптимизации!
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Первые 1
| РегистрСведений.СостояниеДокумента.Состояние КАК Состояние
|ИЗ
| РегистрСведений.СостояниеДокумента КАК РегистрСведенийСостояниеДокумента
|ГДЕ
| РегистрСведенийСостояниеДокумента.Документ = &СсылкаНаДокумент";
Запрос.УстановитьПараметр("СсылкаНаДокумент", СсылкаНаДокумент);
Результат = Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
Если Результат.Состояние = Перечисления.СостоянияДокументов.Проведен Тогда // Пример условия
ОформлениеСтроки.ЦветФона = Новый Цвет(240, 255, 240); // Светло-зеленый
ИначеЕсли Результат.Состояние = Перечисления.СостоянияДокументов.Черновик Тогда
ОформлениеСтроки.ЦветФона = Новый Цвет(255, 250, 230); // Светло-желтый
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Данный пример демонстрирует принцип, но содержит серьезный недостаток: в цикле для каждой строки выполняется отдельный запрос к базе данных, что приводит к значительным замедлениям на больших списках. Мы рассмотрим, как это оптимизировать.
Как мы уже упоминали, прямолинейный подход, когда внутри цикла ПриПолученииДанных для каждой строки выполняется отдельный запрос к регистру сведений, крайне неэффективен. Это приводит к так называемой "проблеме N+1 запросов", где N – количество видимых строк. На списках, содержащих десятки или сотни строк, это может вызвать очень заметные задержки и "торможение" интерфейса.
Для решения этой проблемы мы применяем оптимизированный алгоритм, который значительно сокращает количество обращений к базе данных:
ПриПолученииДанных мы собираем все ссылки на документы из коллекции ОформленияСтрок в один массив. Это позволяет нам получить список всех документов, чьи строки в данный момент отображаются на экране.ГДЕ Ссылка В (&МассивСсылок). Этот запрос вернет нам всю необходимую информацию о состоянии всех видимых документов за одно обращение к базе данных.ОформленияСтрок. Для каждой строки, используя ее ссылку на документ, мы быстро находим соответствующие данные в подготовленной структуре (таблице значений или соответствии) и применяем необходимое оформление.Этот метод сокращает количество обращений к базе данных до одного (плюс одно, если нужно, для определения других общих параметров), что кардинально ускоряет отрисовку списка и улучшает отзывчивость интерфейса.
Пример оптимизированного кода:
Процедура ДокументСписокПриПолученииДанных(Элемент, ОформленияСтрок)
// 1. Собираем ссылки на документы из всех видимых строк
МассивСсылок = Новый Массив;
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
// Проверяем, что ссылка существует и не пустая
Если ТипЗнч(ОформлениеСтроки.ДанныеСтроки.Ссылка) = Тип("ДокументСсылка.РеализацияТоваровУслуг")
ИЛИ ТипЗнч(ОформлениеСтроки.ДанныеСтроки.Ссылка) = Тип("ДокументСсылка.ПоступлениеТоваровУслуг") Тогда // Указываем типы документов
МассивСсылок.Добавить(ОформлениеСтроки.ДанныеСтроки.Ссылка);
КонецЕсли;
КонецЦикла;
// Проверяем, есть ли ссылки для обработки
Если МассивСсылок.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// 2. Выполняем ОДИН запрос к регистру сведений для всех собранных ссылок
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| РегистрСведенийСостояниеДокумента.Документ КАК Документ,
| РегистрСведенийСостояниеДокумента.Состояние КАК Состояние
|ИЗ
| РегистрСведений.СостояниеДокумента КАК РегистрСведенийСостояниеДокумента
|ГДЕ
| РегистрСведенийСостояниеДокумента.Документ В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
// 3. Выгружаем результат в Соответствие для быстрого доступа
СостояниеПоДокументам = Новый Соответствие;
Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
СостояниеПоДокументам.Вставить(СтрокаРезультата.Документ, СтрокаРезультата.Состояние);
КонецЦикла;
// 4. Повторно перебираем ОформленияСтрок и применяем оформление
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
СсылкаНаДокумент = ОформлениеСтроки.ДанныеСтроки.Ссылка;
СостояниеДокумента = СостояниеПоДокументам.Получить(СсылкаНаДокумент);
Если СостояниеДокумента <> Неопределено Тогда
Если СостояниеДокумента = Перечисления.СостоянияДокументов.Проведен Тогда
ОформлениеСтроки.ЦветФона = Новый Цвет(240, 255, 240); // Светло-зеленый
ИначеЕсли СостояниеДокумента = Перечисления.СостоянияДокументов.Черновик Тогда
ОформлениеСтроки.ЦветФона = Новый Цвет(255, 250, 230); // Светло-желтый
ИначеЕсли СостояниеДокумента = Перечисления.СостоянияДокументов.К_Оплате Тогда
ОформлениеСтроки.ЦветФона = Новый Цвет(255, 230, 230); // Светло-красный
ОформлениеСтроки.ЦветТекста = Новый Цвет(128, 0, 0); // Темно-красный текст
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В этом примере мы также явно проверяем тип ссылки, чтобы избежать ошибок, если в списке могут быть строки, не относящиеся к документам (например, группы). Использование Соответствие вместо ТаблицыЗначений для быстрого поиска по ссылке является предпочтительным способом, так как обеспечивает более высокую скорость доступа к данным.
Хотя для задачи раскрашивания строк свойство ЭлементыФормы.Список.ТекущиеДанные не подходит, существуют ситуации, когда нам действительно нужно программно установить курсор на определенную строку при открытии формы или по другому событию. Например, если мы хотим, чтобы при открытии формы сразу был выделен последний созданный документ или документ, переданный в качестве параметра.
Для программной установки курсора на нужную строку используется свойство ТекущаяСтрока элемента формы списка. Важно понимать разницу между ТекущаяСтрока и ТекущиеДанные:
ТекущаяСтрока – это свойство для записи, которое принимает значение, идентифицирующее строку в источнике данных (например, ссылку на объект). Присваивая этому свойству значение, мы программно выделяем соответствующую строку в интерфейсе.ТекущиеДанные – это свойство только для чтения, которое возвращает структуру данных текущей (выделенной) строки. Его нельзя изменить напрямую, оно меняется как следствие установки ТекущаяСтрока или действий пользователя.Пример установки курсора на первую строку в событии ПриОткрытии:
Процедура ПриОткрытии()
// Проверяем, что список не пустой и содержит данные
Если ЭлементыФормы.ДокументСписок.Значение.Количество() > 0 Тогда
// Получаем первую строку из источника данных табличного поля.
// Источником может быть динамический список, таблица значений и т.д.
// Доступ к элементу по индексу [0] допустим, если источник поддерживает это.
// Для ДинамическогоСписка может потребоваться другой способ получения ссылки
// первой строки, например, через запросы или свойство ТекущаяСтрока,
// которое возвращает идентификатор записи. Для простоты рассмотрим,
// что данные уже загружены и доступны как коллекция.
// Для ДинамическогоСписка на обычных формах, если нужна именно первая видимая строка:
// Можно попробовать получить данные через запросы или использовать свойство
// ТекущаяСтрока ДинамическогоСписка после его инициализации.
// Однако более универсальный способ:
// В данном случае, если список уже отображает данные, можно попытаться установить
// ТекущуюСтроку напрямую по ссылке, если у нас есть способ ее получить.
// Например, если мы знаем, что список отсортирован по дате и хотим выделить самую первую запись.
// Предположим, что мы хотим выделить первую строку, если она есть.
// Для обычных форм и ДинамическогоСписка, свойство "ТекущаяСтрока" элемента
// ЭлементыФормы.ДокументСписок ожидает ИДЕНТИФИКАТОР строки (например, Ссылку).
// Получить первую ссылку из динамического списка без его перебора напрямую сложно.
// Проще всего это сделать после открытия, когда список уже заполнен.
// Более надежный способ для ДинамическогоСписка:
// Если вам действительно нужна первая строка сразу при открытии,
// возможно, придется выполнить отдельный запрос для получения первой ссылки.
ЗапросПервойСсылке = Новый Запрос;
ЗапросПервойСсылке.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
| Документ.Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Документ
|УПОРЯДОЧИТЬ ПО
| Документ.Дата УБЫВ,
| Документ.Номер УБЫВ"; // Пример сортировки
Результат = ЗапросПервойСсылке.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
ЭлементыФормы.ДокументСписок.ТекущаяСтрока = Результат.Ссылка;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот код выполнится на клиенте и установит курсор на первую строку в списке, после чего ТекущиеДанные сразу станут определены. Также для автоматического позиционирования на первую или последнюю запись можно использовать свойство табличного поля НачальноеОтображениеСписка (например, установить в "Конец"), чтобы курсор вставал на соответствующую запись. Сообщение 11 на форуме указывает, что установка некоторых флагов ("На закладке Расположение") может влиять на заполнение ПриПолученииДанных, но не ПриОткрытии, что подтверждает необходимость программной установки, если это требуется именно в ПриОткрытии.
Мы подробно разобрали, почему свойство ЭлементыФормы.Список.ТекущиеДанные возвращает "Неопределено" при открытии формы списка в 1С на обычных формах – это связано с тем, что оно отражает *активно выбранную* строку пользовательского интерфейса. Мы выяснили, что для задачи раскрашивания строк по условию следует использовать событие ПриПолученииДанных и коллекцию ОформленияСтрок, а не пытаться работать с ТекущиеДанные.
Кроме того, мы рассмотрели критически важный аспект оптимизации производительности: вместо выполнения множества запросов к базе данных внутри цикла, мы предложили собирать ссылки, делать один общий запрос и затем применять данные. Наконец, мы коснулись программной установки текущей строки с помощью свойства ТекущаяСтрока, что может быть полезно в других сценариях, но не является решением для массового оформления строк. Применяя эти знания, вы сможете создавать более эффективные и отзывчивые формы списков в 1С.