Консультация Беларусь
Консультация Россия
Блог

Как показать картинку на форме

Представим, что в нашей конфигурации есть справочник "Товары":
Для каждого товара нужно иметь возможность загрузить в 1С его изображение, и показывать его на форме товара:
Задача с виду понятная и простая, но её реализация совсем неочевидная.
1. Как хранить изображение в 1С
Первое, что вы должны понимать: любой файл можно представить в виде последовательности байт, и изображение - не исключение:
Для хранения двоичных данных в 1С используется тип данных ХранилищеЗначения:
Для того чтобы загрузить изображение в 1С, нужно:
  • получить двоичные данные изображения
  • поместить двоичные данные в реквизит с типом ХранилищеЗначения
Тогда для выгрузки изображение из 1С в файл последовательность действий будет такой:
  • из реквизита с типом ХранилищеЗначения получить двоичные данные файла
  • записать двоичные данные в файл
Хранить двоичные данные файла в справочнике "Товары" не самая лучшая идея, потому что:
  • это замедлит чтение данных из справочника: например, при программном получении данных объекта (метод ПолучитьОбъект()), или при обращении к реквизитам товара через точку (Товар.Артикул) из базы данных будут получены не только значения реквизитов справочника, но и картинка, а её размер может быть немаленьким
  • кроме двоичиных данных файла важно хранить его расширение и другие сведения, например, размер, дату изменения, т.е. данные, которые напрямую к товару не имеют отношения
Для хранения изображений (а также других файлов, связанных с товарами) лучше всего использовать отдельную таблицу, например, справочник или регистр сведений.
Рассмотрим реализацию с помощью отдельного справочника, назовём его ТоварыПрисоединенныеФайлы:
Для этого справочника установим в качестве владельца справочник Товары:
Теперь в справочник Товары добавим реквизит ФайлКартинки, в котором будем хранить ссылку на справочник присоединенных файлов:
2. Как показать картинку на форме
Чтобы показать в 1С картинку на форме товара нужно:
  • создать реквизит формы АдресКартинки с типом Строка
  • создать элемент формы вида Поле картинки, связанный с реквизитом АдресКартинки
  • описать программный код заполнения реквизита АдресКартинки: его нужно заполнить либо адресом во временном хранилище, по которому находятся двоичные данные картинки, либо навигационной ссылкой на реквизит, в котором хранятся двоичные данные картинки
Сначала поработаем с формой в редакторе: добавим реквизит и элемент формы, и расположим поле картинки справа от основных реквизитов товара, используя группы.
Теперь поработаем с кодом.
Обработаем событие Нажатие, связанное с полем картинки:
При нажатии на поле картинки:
  • если картинка уже выбрана, будем показывать её пользователю
  • если картинка ещё не выбрана, будем показывать диалог для выбора картинки, а потом выбранную картинку помещать во временное хранилище
&НаКлиенте
Процедура АдресКартинкиНажатие(Элемент, СтандартнаяОбработка)
	
	СтандартнаяОбработка = Ложь;     
	
	Если ЗначениеЗаполнено(Объект.ФайлКартинки) Тогда
		ПоказатьКартинку();
	Иначе
		ВыбратьКартинку();
	КонецЕсли;
	
КонецПроцедуры
Код процедуры ВыбратьКартинку():
&НаКлиенте
Асинх Процедура ВыбратьКартинку()
	
	ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
	ПараметрыДиалога.Заголовок = "Выберите картинку";
	ПараметрыДиалога.Фильтр = "Все файлы изображений | *.jpg; *.png; *.bmp";
	
	ОписаниеФайла = Ждать ПоместитьФайлНаСерверАсинх(,,,ПараметрыДиалога,УникальныйИдентификатор);
	Если ОписаниеФайла <> Неопределено Тогда
		
		АдресКартинки = ОписаниеФайла.Адрес;
		
		ИнформацияОФайле = Новый Структура;
		ИнформацияОФайле.Вставить("Адрес", АдресКартинки);
		ИнформацияОФайле.Вставить("Имя", ОписаниеФайла.СсылкаНаФайл.Имя);
		ИнформацияОФайле.Вставить("Расширение", ОписаниеФайла.СсылкаНаФайл.Расширение);
		ИнформацияОФайле.Вставить("Размер", ОписаниеФайла.СсылкаНаФайл.Размер());
		
		Объект.ФайлКартинки = СоздатьПрисоединенныйФайл(Объект.Ссылка, ИнформацияОФайле);
		
	КонецЕсли;
	
КонецПроцедуры
Для помещения файла во временное хранилище с предварительным показом диалога выбора файла в приведенном выше коде используется асинхронный метод ПоместитьФайлНаСерверАсинх().
После помещения файла на сервер, адрес, по которому находятся данные картинки во временном хранилище помещается в реквизит формы АдресКартинки, а затем вызывается функция, в которой происходит создание элемента в справочнике ТоварыПрисоединенныеФайлы, ниже приведен код этой функции:
&НаСервереБезКонтекста
Функция СоздатьПрисоединенныйФайл(Товар, ИнформацияОФайле)
	
	ХранилищеКартинки = Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(ИнформацияОФайле.Адрес), 
													Новый СжатиеДанных(9));
	
	ОбъектФайла = Справочники.ТоварыПрисоединенныеФайлы.СоздатьЭлемент();
	ОбъектФайла.Владелец	 = Товар;
	ОбъектФайла.Наименование = ИнформацияОФайле.Имя;
	ОбъектФайла.Расширение	 = ИнформацияОФайле.Расширение;
	ОбъектФайла.Размер		 = ИнформацияОФайле.Размер;
	ОбъектФайла.ДанныеФайла	 = ХранилищеКартинки;
	ОбъектФайла.ИмяФайла	 = СтрЗаменить(ОбъектФайла.Наименование, ОбъектФайла.Расширение, "");
	
	ОбъектФайла.Записать();
	
	Возврат ОбъектФайла.Ссылка;

КонецФункции // СоздатьПрисоединенныйФайл()
Если картинка товара уже выбрана, то при нажатии на поле картинки вызывается процедура ПоказатьКартинку(), ниже её код:
&НаКлиенте
Асинх Процедура ПоказатьКартинку()

	ДанныеКартинки = ПолучитьДанныеКартинки(Объект.ФайлКартинки);
	
	ПутьКФайлу = ПолучитьИмяВременногоФайла(ДанныеКартинки.Расширение);
	
	Ждать ПолучитьФайлССервераАсинх(ДанныеКартинки.АдресДанных, ПутьКФайлу);
	
	Попытка
		ЗапуститьПриложениеАсинх(ПутьКФайлу);
	Исключение
		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = "Не удалось открыть файл картинки по причине: " + ОписаниеОшибки();
		Сообщение.Сообщить();
	КонецПопытки;
	
КонецПроцедуры // ПоказатьКартинку()
С помощью функции ПолучитьДанныеКартинки() из базы данных выбираются сведения о картинке - Наименование, Расширение, Размер и, самое главное, Адрес во временном хранилище, по которому находятся двоичные данные файла:
&НаСервереБезКонтекста
Функция ПолучитьДанныеКартинки(ФайлКартинки)
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ТоварыПрисоединенныеФайлы.Наименование КАК Наименование,
		|	ТоварыПрисоединенныеФайлы.ДанныеФайла КАК АдресДанных,
		|	ТоварыПрисоединенныеФайлы.ИмяФайла КАК ИмяФайла,
		|	ТоварыПрисоединенныеФайлы.Расширение КАК Расширение,
		|	ТоварыПрисоединенныеФайлы.Размер КАК Размер
		|ИЗ
		|	Справочник.ТоварыПрисоединенныеФайлы КАК ТоварыПрисоединенныеФайлы
		|ГДЕ
		|	ТоварыПрисоединенныеФайлы.Ссылка = &Ссылка";
	
	Запрос.УстановитьПараметр("Ссылка", ФайлКартинки);
	
	РезультатЗапроса = Запрос.Выполнить();	
	
	Выборка = РезультатЗапроса.Выбрать();
	Выборка.Следующий();
	
	СтруктураДанных = Новый Структура;
	Для каждого Колонка Из РезультатЗапроса.Колонки Цикл
		
		ИмяКолонки = Колонка.Имя;          
		Если ИмяКолонки = "АдресДанных" Тогда
			ЗначениеКолонки = ПоместитьВоВременноеХранилище(Выборка[ИмяКолонки].Получить());
		Иначе              
			ЗначениеКолонки = Выборка[ИмяКолонки];
		КонецЕсли;
		
		СтруктураДанных.Вставить(ИмяКолонки, ЗначениеКолонки);	  
		
	КонецЦикла;
	
	Возврат СтруктураДанных;

КонецФункции // ПолучитьДанныеКартинки()
Теперь мы видим на форме картинку:
Но этого недостаточно: если теперь закрыть форму товара, а потом открыть снова, картинка пропадет:
Это связано с тем, что при открытии формы товара реквизит АдресКартинки сейчас не заполняется. Исправим это: создадим обработчик события формы ПриЧтенииНаСервере, и в нём будем заполнять реквизит АдресКартинки навигационной ссылкой на реквизит "ДанныеФайла" справочника ТоварыПрисоединенныеФайлы:
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
	
	АдресКартинки = ПолучитьНавигационнуюСсылку(ТекущийОбъект.ФайлКартинки, "ДанныеФайла");
	
КонецПроцедуры
Теперь при повторном открытии товара изображение на форме отображается:
Добавим возможность удаления изображения товара. Для этого создадим команду формы УдалитьИзображение, и расположим её в контекстном меню поля картинки:
В коде обработчика команды будем помечать на удаление элемент справочника присоединенных файлов, в котором хранятся двоичные данные изображения, а также очистить реквизит формы АдресКартинки и реквизит объекта (товара) ФайлКартинки:
&НаКлиенте
Процедура УдалитьИзображение(Команда)
	
	ПометитьНаУдалениеПрисоединенныйФайл(Объект.ФайлКартинки);
	Объект.ФайлКартинки = Неопределено;
	АдресКартинки = "";
	
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ПометитьНаУдалениеПрисоединенныйФайл(ПрисоединенныйФайл)

	Если НЕ ЗначениеЗаполнено(ПрисоединенныйФайл) Тогда
		Возврат;
	КонецЕсли;
	
	ПрисоединенныйФайлОбъект = ПрисоединенныйФайл.ПолучитьОбъект();
	ПрисоединенныйФайлОбъект.УстановитьПометкуУдаления(Истина);
	ПрисоединенныйФайлОбъект.Записать();

КонецПроцедуры
Готово, теперь пользователь может удалить изображение:
По ссылке ниже вы можете скачать выгрузку демонстрационной информационной базы, в которой реализован пример, описанный выше: https://clck.ru/34d5vK
Присоединяйтесь к нам в Telegram:
https://t.me/ironskills_community1c

Если вы хотите разобраться во всех ключевых механизмах платформы, научиться разрабатывать собственные конфигурации 1С и дорабатывать существующие, приходите на наш комплексный курс по программированию в 1С: Узнать подробнее