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

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

При разработке расширений для типовых конфигураций, таких как УТ 11.5, ERP или КА, часто возникает необходимость дополнить стандартные журналы документов визуальными индикаторами. Например, вывести иконку скрепки, если к документу присоединены файлы — для этого подойдёт расширение для вывода значка скрепки в журналах. В данной статье мы подробно разберем, как правильно добавить программную колонку в ДинамическийСписок и как эффективно заполнить её данными, избегая проблем с производительностью и контекстом выполнения.

Постановка задачи и начальная подготовка формы

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

Рассмотрим пример кода, в котором мы модифицируем текст запроса динамического списка, добавляя «пустое» поле. Иногда параллельно требуется вернуть кнопку "Связанные документы" на командную панель. Проанализируем ситуацию: нам нужно вставить поле в начало выборки SELECT.


&НаСервере
&После("ПриСозданииНаСервере")
Процедура РВ_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
    
    // Модифицируем текст запроса списка
    // Добавляем пустое строковое поле, которое будет служить контейнером для картинки
    ТекстЗапроса = СписокДокументыЗакупки.ТекстЗапроса;
    стрНачалоЗапроса = Сред(ТекстЗапроса, 1, 7); // "ВЫБРАТЬ"
    стрНовСтрока = " """" КАК ПрисоединенныйФайл,";
    стрОкончаниеЗапроса = Сред(ТекстЗапроса, 8);
    
    СписокДокументыЗакупки.ТекстЗапроса = стрНачалоЗапроса + стрНовСтрока + стрОкончаниеЗапроса;
    
    // Программное добавление элемента формы
    НоваяКолонка = Элементы.Добавить("ПрисоединенныйФайл", Тип("ПолеФормы"), Элементы.СписокДокументыЗакупки);
    НоваяКолонка.Вид = ВидПоляФормы.ПолеКартинки;
    НоваяКолонка.ПутьКДанным = "СписокДокументыЗакупки.ПрисоединенныйФайл";
    НоваяКолонка.Заголовок = "Файлы";
    
КонецПроцедуры

Важный нюанс: при программном изменении запроса убедитесь, что поле доступно для использования. Так же можно реализовать индикатор прогресса в ячейке списка — для этого удобно использовать компонент вывода настраиваемых индикаторов в формы списков. Для надежности можно программно установить свойство ИспользоватьВсегда для нового поля в компоновке данных списка.

Особенности работы обработчика ПриПолученииДанныхНаСервере

Для заполнения созданной колонки используется событие ПриПолученииДанныхНаСервере. Это событие вызывается платформой при отрисовке строк списка на экране пользователя (например, при просмотре картинок-вложений в УНФ). Основная сложность заключается в том, что в расширениях этот метод часто вызывается с директивой &НаСервереБезКонтекста.

Разберем, почему это важно. Директива «Без контекста» означает, что нам недоступны реквизиты формы и её элементы напрямую через ЭтотОбъект или Элементы. Вся работа должна строиться исключительно на данных, переданных в параметрах процедуры.

Посмотрим на параметры процедуры ПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки):

Правильная реализация вывода картинок в цикле

Многие разработчики совершают ошибку, пытаясь обратиться к данным строки через точку от ключа, например: строкаСписка.ПрисоединенныйФайл. Это не сработает, так как ключ — это просто ссылка (UUID или Ссылка на документ).

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


&НаСервереБезКонтекста
&После("СписокДокументыЗакупкиПриПолученииДанныхНаСервере")
Процедура РВ_СписокДокументыЗакупкиПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Получаем массив ключей (ссылок на документы), которые сейчас отображаются на экране
    МассивСсылок = Строки.ПолучитьКлючи();
    
    // В идеале здесь должен быть ОДИН запрос для получения информации о файлах для всех ссылок сразу
    // Для примера просто покажем принцип обхода коллекции
    
    Для Каждого КлЗн Из Строки Цикл
        // КлЗн.Ключ - это Ссылка на документ
        // КлЗн.Значение - это объект ДанныеСтрокиДинамическогоСписка
        
        ДанныеСтроки = КлЗн.Значение.Данные;
        
        // Проверяем тип, если журнал содержит разные документы
        Если ТипЗнч(КлЗн.Ключ) = Тип("ДокументСсылка.ПриобретениеТоваровУслуг") Тогда
            
            // Здесь должна быть логика проверки наличия файла
            // Например, если файл есть, устанавливаем картинку
            ДанныеСтроки.ПрисоединенныйФайл = БиблиотекаКартинок.Скрепка;
            
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Оптимизация производительности: избегаем запросов в цикле

Проанализируем критическую ошибку производительности. Если внутри цикла Для Каждого выполнять запрос к базе данных для каждой строки, то при прокрутке списка интерфейс будет «замирать». Если в списке 50 строк, система выполнит 50 маленьких запросов. Для отладки и поиска подобных проблем производительности пригодятся инструменты разработчика для оптимизации кода и запросов 1С.

Правильный алгоритм оптимизации:

  1. Соберите все ссылки из параметра Строки.ПолучитьКлючи() в массив.
  2. Выполните один запрос к таблице присоединенных файлов (например, к справочнику ПриобретениеТоваровУслугПрисоединенныеФайлы), используя условие ГДЕ ВладелецФайла В (&МассивСсылок).
  3. Результат запроса выгрузите в соответствие (ключ — владелец, значение — истина/ложь или индекс картинки).
  4. В основном цикле обхода Строки просто получайте данные из подготовленного соответствия.

Посмотрим, как это выглядит в коде:


// Внутри обработчика ПриПолученииДанныхНаСервере
МассивСсылок = Строки.ПолучитьКлючи();

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   Файлы.ВладелецФайла КАК Владелец
    |ИЗ
    |   Справочник.ПриобретениеТоваровУслугПрисоединенныеФайлы КАК Файлы
    |ГДЕ
    |   Файлы.ВладелецФайла В(&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);

// Заполняем вспомогательное соответствие для быстрого поиска
ДокументыСФайлами = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    ДокументыСФайлами.Вставить(Выборка.Владелец, Истина);
КонецЦикла;

// Теперь заполняем данные строк без обращений к БД
Для Каждого КлЗн Из Строки Цикл
    Если ДокументыСФайлами[КлЗн.Ключ] <> Неопределено Тогда
        КлЗн.Значение.Данные.ПрисоединенныйФайл = БиблиотекаКартинок.Скрепка;
    КонецЕсли;
КонецЦикла;

Работа с картинками: индексы против объектов

Важно помнить, что поле формы с видом ПолеКартинки может работать в двух режимах:

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

Рассмотрим ситуацию с БСП. В конфигурациях на базе БСП (как УТ 11.5) уже часто реализованы механизмы для отображения скрепок. Проверьте, нет ли в динамическом списке поля НаличиеФайлов. Если оно есть, достаточно просто вывести его на форму и настроить отображение картинки в свойствах элемента, не прибегая к сложному кодингу в ПриПолученииДанныхНаСервере.

Подводя итог, отметим, что использование расширений требует четкого понимания контекста выполнения. Метод ПриПолученииДанныхНаСервере является мощным инструментом для визуализации, но требует ответственного подхода к количеству запросов к базе данных, чтобы работа пользователя в журнале документов оставалась комфортной и быстрой.

← На главную