Динамические реквизиты в форме списков 1с8

Часто в списках справочников и документов нужно выводить колонки с дополнительной информацией. Рассказываю, как это сделать.

Сначала изложу классический подход, а затем покажу способ, где детали скрыты от программиста, и который очень удобно применять на практике.

Классический подход

Например, у меня был список документов, и для каждого нужно было выводить динамически статус из регистра сведений.

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

Вообще, при решении задачи вы должны максимально заботиться об эффективности. Все что можно кэшировать - кэшировать.

Сначала напишем запрос, который будет получать из регистра сведений статусы:

ВЫБРАТЬ

 Т.Объект КАК Ссылка,

 Т.Статус КАК Статус,

 Т.Статус.Пиктограмма КАК Пиктограмма,

 Т.Статус.Наименование КАК ПредставлениеСтатуса

ИЗ

 РегистрСведений.ИсторияРеквизитов_Документ_ТаблицаДляЗагрузки.СрезПоследних(&Дата, Объект В (&Ссылки)) КАК Т

Как видно, в запросе мы получили не только объект и его статус, но и представление объекта, и его пиктограмму, чтобы не получать это потом при переборе объектов (это будет медленнее, т.к. объект будет весь считываться в память).

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

Ссылки = Новый Массив();

Соо = Новый Соответствие();

//Получаем все ссылки, которые сейчас видны на экране

Для Каждого ОформлениеСтроки ИЗ ОформленияСтрок Цикл

 Ссылки.Добавить(ОформлениеСтроки.ДанныеСтроки.Ссылка);

 Соо.Вставить(ОформлениеСтроки.ДанныеСтроки.Ссылка, ОформлениеСтроки);

КонецЦикла;

 

//Запрос лучше закэшировать,чтобы не тратиь время на компиляцию, у нас таки и сделано

З = ПолучитьЗапрос(зп_ПолучитьСтатусыТаблицДляЗагрузки);

З.УстановитьПараметр("Дата", ТекущаяДата());

З.УстановитьПараметр("Ссылки", Ссылки);

//Никаких выгрузок в ТЗ, максимум производительности - выборка

Выборка = З.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

 //Получаем оформление по ссылке

 ОформлениеСтроки = Соо[Выборка.Ссылка];

 

 //Оформляем

 

 //Попытка //Попытка-исключение удобно использовать для отладки, но потом лучше убрать, торможение на 5%

  Статус = Выборка.Статус;

  ОформлениеСтроки.Ячейки.Статус.УстановитьКартинку(ПолучитьНастройкуКартинки(Выборка.Пиктограмма));

  ОформлениеСтроки.Ячейки.Статус.УстановитьТекст(Выборка.ПредставлениеСтатуса);

 //Исключение

  Сообщить(ОформлениеСтроки);

 //КонецПопытки;

 //Помечаем что уже обработали

 Соо[Выборка.Ссылка] = Неопределено;

КонецЦикла;

 

//Перебираем остальные строки

Для Каждого Эл Из Соо Цикл

 Если Эл.Значение = Неопределено Тогда

  Продолжить;

 КонецЕсли;

 //Если статус не заполнен

 ОформлениеСтроки.Ячейки.Статус.УстановитьТекст("-");

КонецЦикла;

Пройдемся по алгоритму.

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

После этого выполняем запрос к регистру по отобранным ссылкам. Как видно в запросе, отбор я включаю сразу в параметры виртуальной таблицы - все в угоду эффективности.

Далее, перебираем выборку, по соответствию находим оформление, и оформляем. При оформлении у меня получилось не обращаться к реквизитам объектов из запроса - все нужные реквизиты получены в запросе - опять же для быстродействия.

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

Если нужно для одного объекта выводить данные из нескольких регистров и помечать неоформленные объекты, можно клонировать соответствия, чтобы для каждого регистра иметь свое оформление.

Удобный подход

Второй подход изобрел я, раньше ни у кого его не встречал. Очевидно потому, что программисты редко задумываются о повторном использовании кода.

Суть в том, что технические детали реализации связи данных с оформлением скрыты от программиста. Все, что от него требуется – это составить запрос для извлечения данных и написать, как оформлять данные. Все. А вся внутренняя начинка скрыта в одной функции. В нее вложены всевозможные хитрости для ускорения кода, о которых прикладному программисту можно и не знать.

Итак, прикладной программист должен сделать следующее:

1.       Составить запрос с одним ключевым полем, по умолчанию его название «Ссылка». В запросе должен быть отбор по параметру Ссылки. В него передаются все ключевые поля объектов, которые на данный момент показываются на экране.

2.       Из события ПриПолученииДанных вызвать функцию ПолучитьДанные, передать в нее:

a.        Элемент – элемент, в котором произошло событие

b.      Оформления строк – передать параметр события

c.       Запрос - именно объект Запрос, а не текст запроса. Все параметры должны быть заполнены. Параметр Ссылки заполняется функцией сам.

d.      Параметры – необязательный параметр. Структура, в поле Ключ можно указать другое название ключевого поля, не ссылка.

3.         Функция возвращает таблицу значений, с колонками:

a.       Ключ – значение ключа

b.      Данные – строка результата запроса, соответствующая ключу (строка таблицы значений)

c.       Оформление – оформление, соответствующее ключу

d.      Есть данные – если данных нет, то ложь, иначе истина.

4.       Теперь достаточно перебрать строки полученной таблицы значений и оформить строки на экране при выводе, как нужно.

Единственно, пришлось выгружать результат запроса в таблицу значений, т.к. на строку выборки нельзя сослаться. Но утешает, что на экране помещается не более 50 строк, так что эта выгрузка должна осуществляться быстро.

Последнюю версию функции ищите в моей библиотеке функций в модуле Динамические колонки. Текст функции:

Функция ПолучитьДанные(Элемент, ОформленияСтрок, Запрос, Параметры = Неопределено) Экспорт

                //Запрос должен иметь параметр Ссылки

                Перем Ключ, ТЗ;

               

                ТЗ = Новый ТаблицаЗначений();

                ТЗ.Колонки.Добавить("Ключ");

                ТЗ.Колонки.Добавить("Данные");

                ТЗ.Колонки.Добавить("Оформление");

                ТЗ.Колонки.Добавить("ЕстьДанные", Новый ОписаниеТипов("Булево"));

                Ключ = бфКоллекции.СвойствоСтруктуры(Параметры, "Ключ", "Ссылка");

                Ключи = Новый Массив();

                Соо = Новый Соответствие();

                //Получаем все ссылки, которые сейчас видны на экране

                Для Каждого ОформлениеСтроки ИЗ ОформленияСтрок Цикл

                               ТекКлюч = ОформлениеСтроки.ДанныеСтроки[Ключ];

                               Ключи.Добавить(ТекКлюч);

                               Соо.Вставить(ТекКлюч, ОформлениеСтроки);

                КонецЦикла;

                Запрос.УстановитьПараметр("Ссылки", Ключи);

                ТЗВыборка = Запрос.Выполнить().Выгрузить();

                Для Каждого СтрокаВыборки ИЗ ТЗВыборка Цикл

                               НСтр = ТЗ.Добавить();

                               НСтр.Данные = СтрокаВыборки;

                               ТекКлюч = СтрокаВыборки[Ключ];

                               НСтр.Оформление = Соо[ТекКлюч]; //Получаем оформление по ссылке

                               НСтр.Ключ = ТекКлюч; //Указываем ключевое поле

                               НСтр.ЕстьДанные = истина; //Данные есть

                               Соо[ТекКлюч] = Неопределено;

                КонецЦикла;

                //Перебираем остальные строки

                Для Каждого Эл Из Соо Цикл

                               Если Эл.Значение = Неопределено Тогда

                                               Продолжить;

                               КонецЕсли;

                               НСтр = ТЗ.Добавить();

                               НСтр.Оформление = Эл.Значение; //Получаем оформление

                               НСтр.Ключ = Эл.Ключ; //Указываем ключевое поле

                               НСтр.ЕстьДанные = ложь; //Данных нет

                КонецЦикла;

                Возврат ТЗ;

КонецФункции

Вот как может выглядеть код оформления, согласитесь, это выглядит просто, а работает быстро:

                ТЗ = бфДинамическиеКолонки.ПолучитьДанные(Элемент, ОформленияСтрок, гмЗапрос);

                Для Каждого Стр ИЗ ТЗ Цикл

                               Если Стр.ЕстьДанные Тогда

                                               Цена = Стр.Данные.Цена;

                                               ТекстЦены = Формат(Цена, "ЧН=-");

                                               Стр.Оформление.Ячейки.Цена.УстановитьТекст(ТекстЦены);

                               Иначе

                                               Стр.Оформление.Ячейки.Цена.УстановитьТекст("-");

                               КонецЕсли;

                КонецЦикла;

Заключение

Надеюсь, мой опыт будет вам полезен.

Если вдруг по каким-то причинам после вашего кода форма списка начала тормозить, воспользуйтесь замером производительности в отладчике – найдите, и устраните узкие места, и все будет красиво.