Как в динамическом списке 1С отобразить картинки из внешней системы?

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

Рассмотрим распространенную задачу: необходимо в форме списка (например, в списке номенклатуры или контрагентов) отобразить для каждой строки изображение, которое хранится не в базе 1С, а во внешней системе — на веб-сервисе, FTP-сервере или в сетевой папке. Основная сложность заключается в том, чтобы сделать это для всех видимых строк одновременно, не создавая при этом значительных "тормозов" в интерфейсе пользователя при прокрутке списка.

Проанализируем несколько подходов, от самого простого и очевидного до наиболее производительного и отказоустойчивого.

Решение 1: Прямая загрузка в событии списка

Этот метод является самым быстрым в реализации, но, как мы увидим, имеет серьезные недостатки в производительности. Идея заключается в том, чтобы "на лету" подгружать картинки в момент, когда платформа запрашивает данные для отображения.

Разберем по шагам:

  1. Подготовка формы. В конфигураторе откройте нужную форму списка. В дереве элементов найдите ваш Динамический список, раскройте его и добавьте новую колонку. Для этой колонки не нужно указывать путь к данным. Мы будем заполнять ее программно.
  2. Настройка колонки. Выделите добавленную колонку и в палитре свойств установите ей тип Строка, а свойство Вид установите в значение "Поле картинки". Это скажет платформе, что в эту колонку мы будем помещать не текст, а адрес картинки.
  3. Создание обработчика события. Выделите сам Динамический список (не колонку) и в палитре свойств перейдите на вкладку "События". Найдите событие ПриПолученииДанныхНаСервере и создайте для него обработчик.

Это событие возникает на сервере каждый раз, когда форме требуется новая порция данных для отображения (при открытии, при скроллинге вниз и т.д.). Именно здесь мы и будем загружать наши изображения.

Посмотрим на пример кода в обработчике:


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

    // Элементы - это коллекция строк, которые будут отображены.
    // Переберем их, чтобы для каждой строки получить и установить картинку.
    Для Каждого Элемент Из Элементы Цикл

        // Получаем уникальный идентификатор строки, например, ссылку на объект.
        // Предположим, что ключ для получения картинки - это Артикул номенклатуры.
        ДанныеСтроки = Элемент.Значение;
        КлючКартинки = ДанныеСтроки.Артикул;

        // Здесь должна быть ваша функция, которая по ключу получает двоичные данные картинки
        // из внешней системы (например, через HTTP-запрос).
        // Пример: ДвоичныеДанныеКартинки = ОбщийМодуль.ПолучитьКартинкуПоАртикулу(КлючКартинки);
        ДвоичныеДанныеКартинки = Неопределено; // Замените эту строку своей логикой

        Если ДвоичныеДанныеКартинки <> Неопределено Тогда
            // Если данные получены, создаем объект Картинка
            Картинка = Новый Картинка(ДвоичныеДанныеКартинки);
            
            // Помещаем картинку во временное хранилище и получаем ее адрес
            АдресКартинки = ПоместитьВоВременноеХранилище(Картинка, УникальныйИдентификатор);
            
            // Записываем полученный адрес в нашу новую колонку
            ДанныеСтроки.ИмяВашейКолонки = АдресКартинки;
        КонецЕсли;

    КонецЦикла;

КонецПроцедуры

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

Решение 2: Оптимизация с помощью кэширования

Чтобы избежать повторной загрузки одних и тех же картинок при каждом скроллинге, внедрим механизм кэширования. Кэш может быть разным, но самый простой — кэш на время сеанса работы пользователя.

Разберем по шагам:

  1. Добавим в реквизиты формы новый реквизит, например, КэшАдресовКартинок с типом Соответствие. Этот реквизит будет жить только пока открыта форма.
  2. Модифицируем наш обработчик ПриПолученииДанныхНаСервере.

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Инициализируем наш кэш при создании формы
    КэшАдресовКартинок = Новый Соответствие;
КонецПроцедуры

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

    Для Каждого Элемент Из Элементы Цикл
        ДанныеСтроки = Элемент.Значение;
        КлючКартинки = ДанныеСтроки.Артикул;

        // Сначала проверяем, нет ли картинки в нашем кэше
        Если КэшАдресовКартинок.Получить(КлючКартинки) <> Неопределено Тогда
            // Картинка уже была загружена ранее, берем адрес из кэша
            ДанныеСтроки.ИмяВашейКолонки = КэшАдресовКартинок[КлючКартинки];
        Иначе
            // Картинки в кэше нет, загружаем ее извне
            ДвоичныеДанныеКартинки = ОбщийМодуль.ПолучитьКартинкуПоАртикулу(КлючКартинки);
            
            Если ДвоичныеДанныеКартинки <> Неопределено Тогда
                АдресКартинки = ПоместитьВоВременноеХранилище(Новый Картинка(ДвоичныеДанныеКартинки), УникальныйИдентификатор);
                
                // Записываем адрес и в колонку, и в кэш для будущего использования
                ДанныеСтроки.ИмяВашейКолонки = АдресКартинки;
                КэшАдресовКартинок.Вставить(КлючКартинки, АдресКартинки);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

Этот подход значительно улучшит ситуацию. "Тормоза" будут только при первой загрузке порции данных. При повторном просмотре тех же строк картинки будут браться из кэша мгновенно. Однако первая загрузка по-прежнему остается блокирующей.

Решение 3: Асинхронная загрузка (рекомендуемый способ)

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

Общий алгоритм такой:

  1. При отображении строки списка мы инициируем фоновую загрузку картинки.
  2. Фоновое задание скачивает картинку, обрабатывает ее (например, приводит к стандартному размеру) и сохраняет в постоянный кэш (например, в специальный регистр сведений).
  3. Форма списка с некоторой периодичностью проверяет кэш и, если находит готовую картинку для видимой строки, обновляет ее отображение.

Этот механизм сложнее в реализации, так как требует создания фоновых заданий и механизма обновления интерфейса (например, через подключаемый обработчик ожидания), но он обеспечивает наилучший пользовательский опыт.

Ключевые моменты асинхронного подхода:

Дополнительные полезные советы

1. Стандартизируйте размер картинок.

Если внешние изображения имеют разные размеры и пропорции, таблица списка будет выглядеть неаккуратно ("кривая и косая"). Перед помещением картинки во временное хранилище или в кэш, обработайте ее на сервере, приведя к единому размеру. Это можно сделать с помощью объекта Картинка.


// ... получение ДвоичныеДанныеКартинки
ИсходнаяКартинка = Новый Картинка(ДвоичныеДанныеКартинки);

// Изменяем размер картинки, сохраняя пропорции, чтобы она вписалась в квадрат 100x100
ОбработаннаяКартинка = ИсходнаяКартинка.ИзменитьРазмер(100, 100, Истина);

// Получаем двоичные данные уже измененной картинки
ОбработанныеДанные = ОбработаннаяКартинка.ПолучитьДвоичныеДанные();
АдресКартинки = ПоместитьВоВременноеХранилище(Новый Картинка(ОбработанныеДанные));
// ...

2. Добавьте возможность отключения картинок.

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

← На главную