При разработке интерфейсов в среде 1С:Предприятие 7.7 программисты часто сталкиваются с необходимостью динамического отображения графической информации. Классический сценарий — вывод фотографии номенклатуры в форме элемента справочника. Мы загружаем файл на диск, привязываем его к коду товара, но сталкиваемся с неприятным поведением платформы: картинка на форме обновляется только после закрытия и повторного открытия окна. Вызовы стандартных методов перерисовки, таких как Форма.Обновить(), не приносят желаемого результата.
В этой статье мы подробно проанализируем, почему возникает эта проблема, разберем грубую ошибку в логике работы с объектом Картинка и рассмотрим несколько надежных способов мгновенного обновления графики на форме без ее перезапуска.
Давайте подробно рассмотрим код процедуры загрузки изображения, который изначально использовал автор темы:
Процедура _ЗагрузитьКартинку()
DefIMG = _глПутьКартинок+"def.jpg";
ИмяФайла = _глПутьКартинок+Строка(СокрЛП(Код)+".jpg");
Карт = СоздатьОбъект("Картинка");
Карт.УстановитьКартинку(КР);
Карт.РежимРисования(3);
Если ФС.СуществуетФайл(ИмяФайла)=1 Тогда
Карт.Загрузить(ИмяФайла);
Иначе
Карт.Загрузить(DefIMG);
КонецЕсли;
КонецПроцедуры
Выясним причину, по которой этот алгоритм не работает при повторном вызове. Главная проблема кроется в непонимании принципов взаимодействия элементов формы и объектов встроенного языка в 1С 7.7.
На форме элемента справочника уже размещен элемент диалога типа «Картинка» с идентификатором КР. В контексте формы этот идентификатор сам по себе является готовым объектом. Когда мы пишем строку Карт = СоздатьОбъект("Картинка"), мы создаем в оперативной памяти абсолютно новый, независимый и пустой объект графики.
Затем выполняется команда Карт.УстановитьКартинку(КР). Метод УстановитьКартинку() копирует данные из источника (параметра) в объект, для которого он вызван. В этот момент мы скопировали текущее состояние картинки с формы КР в локальную переменную Карт. Связь между ними после этого разрывается — это не ссылка на элемент формы, а независимая копия данных.
Далее код выполняет загрузку файла с диска: Карт.Загрузить(ИмяФайла). Новое изображение успешно считывается в переменную Карт. Процедура завершается, локальная переменная Карт уничтожается сборщиком мусора, а элемент формы КР остается в неизменном виде. Именно поэтому для обновления картинки приходилось закрывать форму и открывать ее заново (при открытии, вероятно, срабатывали другие механизмы инициализации).
Поскольку элемент диалога КР уже сам по себе является объектом типа «Картинка», нам вообще не требуется создавать промежуточный объект в памяти через функцию СоздатьОбъект(). Мы можем загрузить файл напрямую в элемент управления формы. Разберем этот лаконичный и правильный вариант:
Процедура _ЗагрузитьКартинку()
DefIMG = _глПутьКартинок + "def.jpg";
ИмяФайла = _глПутьКартинок + Строка(СокрЛП(Код) + ".jpg");
// Работаем напрямую с элементом формы КР
Если ФС.СуществуетФайл(ИмяФайла) = 1 Тогда
КР.Загрузить(ИмяФайла);
Иначе
КР.Загрузить(DefIMG);
КонецЕсли;
КР.РежимРисования(3); // Устанавливаем режим масштабирования
КонецПроцедуры
В данном случае метод Загрузить() применяется непосредственно к элементу формы. Картинка мгновенно считывается с диска и помещается в элемент интерфейса.
Иногда по архитектурным соображениям или для предварительного анализа параметров файла (например, проверки его корректности до вывода на экран) требуется сначала обработать графику в памяти. В таком случае мы можем использовать временный объект, но в конце процедуры обязательно должны вернуть полученное изображение обратно на форму. Посмотрим, как это реализовать:
Процедура _ЗагрузитьКартинку()
DefIMG = _глПутьКартинок + "def.jpg";
ИмяФайла = _глПутьКартинок + Строка(СокрЛП(Код) + ".jpg");
ВремКарт = СоздатьОбъект("Картинка");
Если ФС.СуществуетФайл(ИмяФайла) = 1 Тогда
ВремКарт.Загрузить(ИмяФайла);
Иначе
ВремКарт.Загрузить(DefIMG);
КонецЕсли;
// Переносим изображение из памяти на элемент диалога формы
КР.УстановитьКартинку(ВремКарт);
КР.РежимРисования(3);
КонецПроцедуры
Здесь ключевым действием является вызов КР.УстановитьКартинку(ВремКарт). Мы берем подготовленный в памяти объект ВремКарт и проецируем его содержимое на экранный элемент КР.
Даже при использовании правильного кода разработчики часто сталкиваются с техническими особенностями старой платформы 1С 7.7, из-за которых картинка все равно может визуально не обновиться или обновиться некорректно. Рассмотрим три важные проблемы и способы их решения.
Штатный механизм отрисовки интерфейса Windows в 1С 7.7 иногда «ленится» обновлять графические области, считая, что элемент формы не изменил своих границ. Метод Форма.Обновить() часто игнорирует графические объекты. Чтобы заставить операционную систему мгновенно перерисовать картинку, используют проверенную хитрость — быстрое переключение видимости элемента:
Форма.КР.Видимость(0);
Форма.КР.Видимость(1);
Этот код заставляет платформу полностью стереть элемент с экрана, а затем нарисовать его заново, гарантируя вывод нового изображения.
Платформа 1С 7.7 оптимизирует работу с памятью и кэширует однажды загруженные файлы. Если пользователь заменяет файл картинки на диске (например, загружает новую фотографию поверх старой с сохранением того же имени файла), программа может проигнорировать изменения и продолжить показывать старую картинку из своего внутреннего кэша.
Чтобы гарантированно очистить кэш перед выводом нового изображения, рекомендуется сначала «обнулить» элемент формы, загрузив в него пустой объект:
ПустаяКарт = СоздатьОбъект("Картинка");
КР.УстановитьКартинку(ПустаяКарт); // Очищаем форму от старых данных
КР.Загрузить(ИмяФайла); // Загружаем обновленный файл
Если в вашей конфигурации процесс устроен так, что пользователь выбирает файл на компьютере, система копирует его в сетевой каталог под именем кода номенклатуры, и затем сразу вызывается процедура _ЗагрузитьКартинку(), может возникнуть конфликт доступа. Копирование файла средствами операционной системы происходит асинхронно и занимает некоторое время.
Если 1С попытается вызвать метод Загрузить() в микросекунду, когда файл еще копируется и заблокирован Windows, произойдет ошибка чтения, и картинка не обновится. Перед загрузкой рекомендуется убедиться, что файл полностью доступен:
// Пример простой паузы или проверки доступности файла
Для НомерПопытки = 1 По 5 Цикл
Попытка
// Пробуем открыть файл в эксклюзивном режиме для проверки готовности
ФС.КопироватьФайл(ИмяФайла, ИмяФайла, 0);
Прервать; // Если ошибки нет, файл готов
Исключение
// Ждем долю секунды, если файл еще занят копированием
Предупреждение("Ожидание готовности файла...", 1);
КонецПопытки;
КонецЦикла;
Применяя данные подходы в совокупности, мы получаем отказоустойчивый и быстрый механизм динамической работы с изображениями. Использование прямой адресации к элементам диалога формы КР.Загрузить() в сочетании с быстрым переключением видимости решает проблему обновления формы раз и навсегда.