При разработке интерфейсов в 1С:Предприятии 8.3 на управляемых формах мы часто сталкиваемся с задачей компактного и информативного отображения нескольких признаков или статусов для одного элемента, например, для товара. Представьте ситуацию, когда у товара может быть несколько статусов одновременно: "Некондиция", "Акция", "Распродажа", "Хит" и так далее. Для каждого такого статуса обычно предусмотрена своя небольшая иконка или картинка. Изначально может возникнуть мысль создать отдельную колонку для каждого признака, но это быстро приведет к перегрузке формы, сделает ее громоздкой и неудобной для восприятия, особенно при большом количестве статусов.
Наша цель — не создавать десять отдельных колонок для десяти признаков, а объединить эти визуальные индикаторы в одной логической колонке динамического списка. Это позволит нам сохранить лаконичность интерфейса и улучшить читаемость информации. Давайте рассмотрим различные подходы к решению этой задачи, которые позволят нам компактно и информативно отображать необходимые статусы, сохраняя при этом гибкость и возможность масштабирования.
Одним из самых простых и быстрых способов реализации нашей задачи, который был упомянут на форуме 1С, является использование встроенного механизма группировки колонок динамического списка в режиме "В ячейке". Этот подход позволяет нам визуально объединить несколько физических колонок в одну логическую ячейку, отображая их содержимое рядом друг с другом. Это отличное решение, если каждый статус уже представлен в виде отдельной колонки с картинкой или булевым значением, которое может быть легко преобразовано в картинку. При необходимости можно воспользоваться примером вывода колонки с картинкой-индикатором в динамическом списке.
Подготовка данных: Прежде всего, нам необходимо убедиться, что для каждого признака, такого как "Некондиция", "Акция" или "Распродажа", у нас в запросе динамического списка или в дополнительных реквизитах есть отдельное поле. Это поле может возвращать либо булево значение (Истина/Ложь), либо непосредственно ссылку на элемент справочника КоллекцииКартинок, либо ДвоичныеДанные самой картинки. Если мы используем булево поле, то на уровне элемента формы Поле для этой колонки мы можем указать ВидПоля = ПолеКартинки. Затем, в свойствах поля, мы привязываем определенную картинку к значению "Истина", а к "Ложь" можем привязать пустую картинку или оставить поле пустым.
Создание группы колонок: В конфигураторе, при редактировании формы динамического списка, мы должны добавить новый элемент управления типа "Группа". Для этой группы устанавливаем свойство Вид = ГруппаКолонок. Это сообщит системе, что данная группа предназначена для организации колонок.
Настройка группировки "В ячейке": После того как мы создали группу типа ГруппаКолонок, следующим шагом будет установка для нее свойства Группировка = ВЯчейке. Это ключевое свойство, которое заставляет содержимое всех вложенных колонок отображаться в рамках одной ячейки строки динамического списка, располагаясь горизонтально.
Размещение колонок: Теперь все колонки, которые мы хотим отобразить рядом в одной логической ячейке, мы должны перенести внутрь этой группы. Мы просто перетаскиваем нужные поля из списка доступных элементов в только что созданную группу. Система автоматически расположит их последовательно в одной ячейке.
Преимущества: Этот метод очень прост в реализации и не требует написания сложного кода. Он идеально подходит для ситуаций, когда нам достаточно простого последовательного отображения иконок без сложного форматирования. Недостатки: Мы имеем меньше гибкости в расположении картинок (они всегда будут идти строго друг за другом). Технически это все еще остаются отдельные колонки, что может иметь нюансы при настройке их ширины или при необходимости более сложного взаимодействия.
Этот подход предоставляет нам значительно большую свободу и контроль над внешним видом наших индикаторов. Основная идея заключается в том, чтобы программно, на стороне сервера, объединить несколько исходных изображений в одно составное изображение. Затем это новое, скомбинированное изображение мы отображаем в одной колонке динамического списка, используя обычное ПолеКартинки.
Разработка функции формирования изображения: Мы начнем с создания серверной функции, которая будет принимать на вход информацию о состоянии признаков товара (например, булевы флаги: Акция, Некондиция, Распродажа) и возвращать ДвоичныеДанные составного изображения. Эту функцию можно разместить в общем модуле, доступном на сервере.
Логика объединения картинок: Внутри этой функции мы последовательно формируем новое изображение. Для этого нам потребуются исходные картинки, которые могут быть предварительно сохранены в макетах конфигурации (например, в общем макете типа "Картинки") или в справочнике КоллекцииКартинок. Мы будем использовать объект Картинка для загрузки исходных изображений и объект РисовальщикКартинок для их отрисовки на новом объекте Картинка нужного размера.
Нам необходимо будет определить общую ширину и высоту составного изображения, исходя из размеров исходных картинок и количества активных признаков. Затем мы поочередно рисуем активные иконки на новой Картинке, располагая их так, как нам нужно (например, горизонтально с небольшим отступом).
Пример кода для формирования составного изображения:
// Примерная концепция формирования составного изображения на сервере
Функция СформироватьСоставнуюКартинку(Акция, Некондиция, Распродажа, Хит) Экспорт
РазмерВысота = 16; // Предположим, стандартная высота иконок 16px
ОтступМеждуКартинками = 2; // Отступ в пикселях между иконками
// Загружаем исходные картинки из БиблиотекиКартинок или макетов
КартинкаАкция = БиблиотекаКартинок.Акция;
КартинкаНекондиция = БиблиотекаКартинок.Некондиция;
КартинкаРаспродажа = БиблиотекаКартинок.Распродажа;
КартинкаХит = БиблиотекаКартинок.ХитПродаж;
// Список активных картинок
СписокАктивныхКартинок = Новый СписокЗначений();
Если Акция Тогда СписокАктивныхКартинок.Добавить(КартинкаАкция); КонецЕсли;
Если Некондиция Тогда СписокАктивныхКартинок.Добавить(КартинкаНекондиция); КонецЕсли;
Если Распродажа Тогда СписокАктивныхКартинок.Добавить(КартинкаРаспродажа); КонецЕсли;
Если Хит Тогда СписокАктивныхКартинок.Добавить(КартинкаХит); КонецЕсли;
Если СписокАктивныхКартинок.Количество() = 0 Тогда
// Если нет активных признаков, возвращаем пустые данные или пустую картинку
Возврат Новый ДвоичныеДанные("");
КонецЕсли;
// Определяем общую ширину составной картинки
ОбщаяШирина = 0;
Для Каждого ЭлементКартинки Из СписокАктивныхКартинок Цикл
Если ОбщаяШирина > 0 Тогда
ОбщаяШирина = ОбщаяШирина + ОтступМеждуКартинками;
КонецЕсли;
ОбщаяШирина = ОбщаяШирина + ЭлементКартинки.Значение.Ширина;
КонецЦикла;
// Создаем новую картинку для рисования
НоваяКартинка = Новый Картинка(ОбщаяШирина, РазмерВысота);
Художник = Новый РисовальщикКартинок(НоваяКартинка);
ТекущаяКоординатаX = 0;
// Рисуем каждую активную картинку на новой
Для Каждого ЭлементКартинки Из СписокАктивныхКартинок Цикл
КартинкаДляОтрисовки = ЭлементКартинки.Значение;
Художник.РисуйКартинку(КартинкаДляОтрисовки, Новый Точка(ТекущаяКоординатаX, 0));
ТекущаяКоординатаX = ТекущаяКоординатаX + КартинкаДляОтрисовки.Ширина + ОтступМеждуКартинками;
КонецЦикла;
Возврат НоваяКартинка.ПолучитьДвоичныеДанные();
КонецФункции
// Пример использования в запросе динамического списка (или при обработке данных):
// В поле запроса мы можем вызвать эту функцию, передав ей булевы флаги:
//
// ВЫБОР
// КОГДА Т.Акция И Т.Некондиция И Т.Распродажа И Т.Хит
// ТОГДА &СформироватьСоставнуюКартинкуИстинаИстинаИстинаИстина
// КОГДА Т.Акция И Т.Некондиция И Т.Распродажа
// ТОГДА &СформироватьСоставнуюКартинкуИстинаИстинаИстинаЛожь
// ...
// ИНАЧЕ &СформироватьСоставнуюКартинкуЛожьЛожьЛожьЛожь
// КОНЕЦ КАК КартинкаСтатусов
// Или, если данных немного и запрос не будет перегружен, можно вызывать напрямую:
// СформироватьСоставнуюКартинку(Т.Акция, Т.Некондиция, Т.Распродажа, Т.Хит) КАК КартинкаСтатусов
// (Однако, вызов функций на каждый элемент списка в запросе может негативно сказаться на производительности для больших объемов данных.
// Лучше предварительно скомбинировать параметры и передавать их через параметры запроса или использовать обработку ПриПолученииДанных.)
Отображение на форме: В динамический список на форме мы добавляем дополнительное поле. Для этого поля мы устанавливаем ВидПоля = ПолеКартинки и указываем путь к данным, которые возвращают сформированные ДвоичныеДанные. Если данные формируются во временном хранилище, то это будет ссылка на хранилище.
Преимущества: Этот метод дает нам полный контроль над внешним видом составного изображения, включая расположение, размеры и отступы между иконками. На клиентской стороне отображается только одна картинка, что способствует высокой производительности интерфейса. Недостатки: Требует написания кода для формирования изображения, что может быть более трудоемким. При массовом переформировании картинок для большого количества строк списка может возникнуть дополнительная нагрузка на сервер.
Метод с использованием поля HTML-документа является одним из самых гибких, поскольку позволяет нам вставлять произвольный HTML-код непосредственно в ячейку динамического списка. Это открывает широкие возможности для форматирования, позволяя нам выводить несколько картинок подряд, комбинировать их с текстом, применять стили CSS и даже включать интерактивные элементы.
Создание дополнительного строкового реквизита: В динамический список или в запрос, на основе которого строится список, мы добавляем новый строковый реквизит. Именно в этот реквизит мы будем записывать сформированный HTML-код для каждой строки.
Формирование HTML-кода: На серверной стороне мы пишем функцию, которая будет формировать строку HTML. Для каждой картинки мы используем тег <img>, указывая путь к изображению через атрибут src. Источники картинок могут быть разнообразными:
src тега <img>. Это позволяет избежать дополнительных запросов к серверу за каждой картинкой.Мы также можем использовать встроенные CSS-стили (атрибут style) для точной настройки внешнего вида, отступов и выравнивания картинок.
Пример формирования HTML-кода:
// Пример формирования HTML-кода для одной ячейки
Функция СформироватьHTMLСтатусов(Акция, Некондиция, Распродажа, Хит) Экспорт
HTMLСтрока = "<div style='display: flex; align-items: center;'>"; // Используем flexbox для выравнивания
// Предполагаем, что у нас есть функция, которая возвращает Base64-строку для картинки
// Например, ПолучитьBase64Картинки("Акция")
Если Акция Тогда
// Здесь мы должны вставить Base64-представление картинки или URL
// Для примера используем заглушку, в реальной системе будет вызов функции
HTMLСтрока = HTMLСтрока + "<img src='data:image/png;base64,...' style='height: 16px; margin-right: 2px;' alt='Акция' />";
КонецЕсли;
Если Некондиция Тогда
HTMLСтрока = HTMLСтрока + "<img src='data:image/png;base64,...' style='height: 16px; margin-right: 2px;' alt='Некондиция' />";
КонецЕсли;
Если Распродажа Тогда
HTMLСтрока = HTMLСтрока + "<img src='data:image/png;base64,...' style='height: 16px; margin-right: 2px;' alt='Распродажа' />";
КонецЕсли;
Если Хит Тогда
HTMLСтрока = HTMLСтрока + "<img src='data:image/png;base64,...' style='height: 16px; margin-right: 2px;' alt='Хит' />";
КонецЕсли;
HTMLСтрока = HTMLСтрока + "</div>"; // Закрываем div
Возврат HTMLСтрока;
КонецФункции
// Пример получения Base64 строки для картинки из БиблиотекиКартинок:
// (Этот код выполняется на клиенте или сервере, в зависимости от контекста)
Функция ПолучитьBase64КартинкиИзБиблиотеки(ИмяКартинки)
Картинка = БиблиотекаКартинок[ИмяКартинки];
Если Картинка = Неопределено Тогда
Возврат "";
КонецЕсли;
ДвоичныеДанные = Картинка.ПолучитьДвоичныеДанные();
СтрокаBase64 = База64Кодировать(ДвоичныеДанные);
Возврат СтрокаBase64;
КонецФункции
Важное замечание: При использовании src='data:image/png;base64,...', строка Base64 может быть очень длинной, что может повлиять на производительность при отображении большого количества строк или при частом обновлении. Мы должны с осторожностью применять этот подход.
Размещение на форме: На форме динамического списка мы создаем элемент управления типа "Поле HTML-документа". Затем мы связываем свойство Данные этого поля с нашим строковым реквизитом, который содержит сформированный HTML-код.
Преимущества: Максимальная гибкость в форматировании, позволяющая создавать сложные визуальные элементы с использованием CSS, комбинируя картинки, текст и другие HTML-элементы. Недостатки: Может быть менее производительным для очень больших списков из-за необходимости парсинга HTML для каждой строки. Существуют некоторые особенности во взаимодействии с полем HTML-документа (например, интерактивность). Требует осторожности с безопасностью, особенно если HTML формируется из внешних данных, чтобы избежать внедрения вредоносного кода (XSS).
Если нам требуется не просто последовательный вывод картинок, а более сложное и структурированное форматирование ячейки, включающее размещение картинок в определенных местах, их комбинирование с текстом, а также применение уникальных стилей для разных частей ячейки, то использование макетов для ячеек динамического списка может оказаться наиболее подходящим решением. Этот метод особенно эффективен для отчетов или сложных табличных частей, где каждая ячейка является мини-документом.
Создание макета: В конфигураторе для нашего динамического списка (или для соответствующего отчета) мы создаем новый макет. Тип макета может быть ТабличныйДокумент или ДвоичныеДанные, в зависимости от конкретной задачи. В макете мы можем графически разместить различные элементы управления, включая ПолеКартинки и ПолеТекста, определяя их положение и размеры.
Определение областей в макете: Внутри созданного макета мы определяем именованные области для каждого элемента, который хотим отобразить. Например, мы можем создать области "КартинкаАкция", "КартинкаНекондиция" и "ТекстНаименования". Размечаем макет, располагая эти области так, как они должны выглядеть в итоговой ячейке.
Программное заполнение макета: Основная логика реализуется в серверном коде, обычно в обработчике события, который вызывается при формировании данных для списка или отчета. Для динамического списка это может быть обработчик ПриВыводеСтроки (если список создан программно) или специальная функция, которая формирует данные ячейки. В этой функции мы программно заполняем определенные области макета в зависимости от условий (статусов товара). Для каждой области типа ПолеКартинки мы указываем ссылку на элемент справочника КоллекцииКартинок или путь к бинарным данным изображения.
Пример логики заполнения макета:
// Примерная логика для обработчика ПриВыводеСтроки в отчете или обработке,
// где Макет - это объект типа Макет.
// Предполагаем, что у нас есть макет с областями, например, "ЯчейкаДанных", "КартинкаАкция", "КартинкаНекондиция".
// В общем модуле или модуле формы/отчета:
// Создаем функцию, которая формирует табличный документ для ячейки
Функция СформироватьЯчейкуСКартинками(ДанныеСтроки) Экспорт
МакетЯчейки = ПолучитьМакет("МойМакет"); // Получаем наш предварительно созданный макет
ОбластьДанных = МакетЯчейки.ПолучитьОбласть("ЯчейкаДанных"); // Область, которая описывает всю ячейку
// Заполняем текстовые поля, если есть
ОбластьДанных.Параметры.Наименование = ДанныеСтроки.Наименование;
// Устанавливаем картинки в зависимости от признаков
Если ДанныеСтроки.Акция Тогда
ОбластьДанных.Рисунки.КартинкаАкция.Картинка = БиблиотекаКартинок.Акция;
Иначе
ОбластьДанных.Рисунки.КартинкаАкция.Картинка = Неопределено; // Скрываем картинку
КонецЕсли;
Если ДанныеСтроки.Некондиция Тогда
ОбластьДанных.Рисунки.КартинкаНекондиция.Картинка = БиблиотекаКартинок.Некондиция;
Иначе
ОбластьДанных.Рисунки.КартинкаНекондиция.Картинка = Неопределено;
КонецЕсли;
// ... и так далее для других картинок
Возврат ОбластьДанных; // Возвращаем заполненную область
КонецФункции
// В обработчике ПриПолученииДанных (для динамического списка) или ПриВыводеСтроки (для отчетов):
// Для каждой строки данных мы вызываем эту функцию и передаем результат в соответствующую колонку,
// которая должна быть типа "Поле HTML-документа" или "Поле ТабличногоДокумента"
//
// Пример для ДинамическогоСписка (при программном формировании данных):
// НаСервере:
// Для Каждого СтрокаТЗ Из МоиДанные Цикл
// НоваяСтрока = Список.Добавить();
// // Заполняем обычные поля
// НоваяСтрока.Наименование = СтрокаТЗ.Наименование;
// // Формируем и привязываем ячейку с макетом
// НоваяСтрока.КолонкаСМакетом = СформироватьЯчейкуСКартинками(СтрокаТЗ);
// КонецЦикла;
Мы должны убедиться, что колонка, в которой мы хотим отобразить результат макета, имеет соответствующий тип элемента формы (Поле HTML-документа или ПолеТабличногоДокумента, если мы используем табличный документ как основу).
Преимущества: Максимальная гибкость в дизайне ячейки. Мы можем создавать визуально сложные структуры, сочетать картинки, текст, различные шрифты и цвета, точно позиционировать элементы. Это отличный вариант для детализированных отчетов и специализированных форм, где требуется высокое качество представления данных. Недостатки: Этот метод наиболее сложен в настройке и разработке. Он требует хорошего понимания работы с макетами и их программного заполнения. Может быть избыточным и неэффективным для простых задач, где достаточно лишь вывести несколько иконок подряд.
На форуме 1С также был предложен более прямолинейный вариант, который заключается в создании всех возможных сочетаний статусов в виде отдельных, заранее подготовленных файлов картинок. Этот метод может быть привлекателен своей кажущейся простотой на первом этапе, но имеет существенные ограничения.
Создание набора картинок: Для каждого возможного сочетания признаков ('Акция', 'Некондиция', 'Акция + Некондиция', 'Акция + Хит' и т.д.) мы вручную или с помощью графического редактора создаем и сохраняем отдельный файл картинки. Например, если у нас 4 признака, то общее количество уникальных комбинаций составит 24 = 16 картинок (включая вариант без признаков).
Хранение картинок: Эти заранее подготовленные комбинированные картинки мы загружаем в справочник КоллекцииКартинок или в макеты конфигурации, присваивая им уникальные и понятные имена (например, "АкцияНекондиция", "ХитРаспродажа").
Привязка к данным: В запросе динамического списка или при программной обработке данных мы определяем, какая именно комбинация признаков соответствует текущей строке. Затем мы возвращаем ссылку на соответствующую, уже готовую комбинированную картинку из БиблиотекиКартинок или макета.
Пример логики определения комбинированной картинки:
// Примерная функция для получения ссылки на комбинированную картинку
Функция ПолучитьКомбинированнуюКартинку(Акция, Некондиция, Распродажа, Хит) Экспорт
ИмяКартинки = "";
Если Акция Тогда ИмяКартинки = ИмяКартинки + "Акция"; КонецЕсли;
Если Некондиция Тогда ИмяКартинки = ИмяКартинки + "Некондиция"; КонецЕсли;
Если Распродажа Тогда ИмяКартинки = ИмяКартинки + "Распродажа"; КонецЕсли;
Если Хит Тогда ИмяКартинки = ИмяКартинки + "Хит"; КонецЕсли;
Если ИмяКартинки = "" Тогда
// Если нет активных признаков, можно вернуть пустую картинку
Возврат БиблиотекаКартинок.ПустаяКартинка;
КонецЕсли;
// Предполагаем, что картинки в БиблиотекеКартинок названы как "АкцияНекондицияРаспродажаХит",
// "АкцияНекондиция", "Хит" и т.д.
Возврат БиблиотекаКартинок[ИмяКартинки];
КонецФункции
Преимущества: Этот метод очень прост в реализации на стороне 1С после того, как все комбинированные картинки созданы. Он обеспечивает минимальную нагрузку на сервер во время выполнения, так как картинки уже готовы и система просто ссылается на них. Недостатки: Этот подход является крайне негибким. При добавлении нового признака или изменении существующего нам придется перерисовывать и перенастраивать абсолютно все существующие комбинации картинок, что становится чрезвычайно трудоемким. Количество комбинаций экспоненциально растет с числом признаков (2N). Этот метод целесообразен только для очень малого и стабильного количества признаков, которые редко меняются.
Как мы видим, существует несколько эффективных способов отобразить несколько картинок подряд в одной колонке управляемой формы 1С. Выбор конкретного метода должен основываться на наших требованиях к гибкости, производительности, сложности реализации и потенциальной масштабируемости:
Каждый из этих методов имеет свои сильные и слабые стороны, и наш выбор должен быть осознанным, исходя из конкретных требований проекта и долгосрочных перспектив его развития. Для базовой настройки источников данных можно использовать обзор способов добавления новых полей в динамические списки.