В разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда данные в дерево значений (или таблицу) программно добавлены, код отработал без ошибок, но пользователь видит пустую форму или старые данные. При этом нажатие клавиши F5 (обновить) мгновенно исправляет ситуацию. Давайте вместе разберем, почему так происходит, и как заставить интерфейс обновляться автоматически без лишних манипуляций со стороны пользователя.
Проанализируем самую частую ошибку. В управляемом приложении данные на форме — это не сами объекты ДеревоЗначений, а специальные структуры данных формы (ДанныеФормыДерево). Когда мы хотим произвести сложные манипуляции с деревом на сервере (например, построить иерархию запросом), мы часто конвертируем реквизит формы в объект ДеревоЗначений. Если после изменений не выполнить обратную операцию, форма «не узнает», что данные внутри объекта изменились.
Рассмотрим правильный алгоритм синхронизации данных:
&НаСервере
Процедура ЗаполнитьДеревоНаСервере()
// 1. Преобразуем реквизит формы в прикладной объект "ДеревоЗначений"
ДеревоОбъект = РеквизитФормыВЗначение("ДеревоНаФорме", Тип("ДеревоЗначений"));
// 2. Производим манипуляции (очистка, заполнение)
ДеревоОбъект.Строки.Очистить();
НоваяСтрока = ДеревоОбъект.Строки.Добавить();
НоваяСтрока.Наименование = "Новый узел";
// 3. КРИТИЧЕСКИ ВАЖНЫЙ ШАГ: Возвращаем данные обратно в реквизит формы
ЗначениеВДанныеФормы(ДеревоОбъект, ДеревоНаФорме);
КонецПроцедуры
Если вы пропустите вызов ЗначениеВДанныеФормы, изменения останутся в локальной переменной ДеревоОбъект, которая будет уничтожена сразу после завершения серверной процедуры. Форма же продолжит отображать старое состояние реквизита ДеревоНаФорме.
Иногда данные в реквизите уже обновились, но графическая оболочка (клиентское приложение) еще не перерисовала элемент управления Таблица, отображающий дерево. В этом случае мы можем вызвать принудительное обновление интерфейса.
Разберем, какой метод использовать на клиенте после возврата с сервера:
&НаКлиенте
Процедура КомандаЗаполнить(Команда)
ЗаполнитьДеревоНаСервере();
// Принудительно обновляем отображение всех данных на форме
ЭтаФорма.ОбновитьОтображениеДанных();
КонецПроцедуры
Важно понимать разницу: ОбновитьОтображениеДанных() просто перерисовывает элементы, основываясь на текущих значениях реквизитов, в то время как ЭтаФорма.Прочитать() перечитывает данные основного объекта из базы данных. Если ваше дерево не является основным реквизитом (например, это просто вспомогательная коллекция), метод Прочитать() вам не поможет. При необходимости программно обновить саму форму документа после изменения его реквизитов сторонними модулями можно использовать инструмент для программного обновления управляемой формы из внешней печатной формы.
Бывают ситуации, когда из-за сложности формы или особенностей платформы стандартные методы обновления «залипают». В таких случаях помогает программная имитация завершения события и запуск обновления в новом интерфейсном потоке. Это делается через ПодключитьОбработчикОжидания.
Посмотрим на пример реализации такого «хака»:
&НаКлиенте
Процедура ПослеЗаполненияДерева()
// Вызываем процедуру обновления через 0.1 секунды
// Это позволит платформе завершить текущие процессы и "встряхнуть" интерфейс
ПодключитьОбработчикОжидания("ПринудительноеОбновлениеИнтерфейса", 0.1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПринудительноеОбновлениеИнтерфейса()
ЭтаФорма.ОбновитьОтображениеДанных();
// Можно также обновить конкретный элемент
Элементы.ДеревоНаФорме.Обновить();
КонецПроцедуры
Выясним причину, если ваше дерево строится не на ДеревоЗначений, а на ДинамическомСписке с иерархическим просмотром. Динамический список читает данные напрямую из таблиц базы данных (СУБД). Если вы программно добавили записи в справочник, но не зафиксировали транзакцию или база данных еще не успела обновить индексы, динамический список может показать пустоту.
В этом случае необходимо использовать метод Элементы.ИмяСписка.Обновить(). Также убедитесь, что в коде создания записей нет незавершенных транзакций (НачатьТранзакцию() без ЗафиксироватьТранзакцию()).
Часто пользователь думает, что дерево не обновилось, потому что после перезаполнения все узлы «схлопнулись». При программном перестроении дерева идентификаторы строк меняются, и платформа не знает, какие узлы были развернуты ранее.
Рассмотрим код, который не только обновляет данные, но и принудительно разворачивает дерево, чтобы данные стали видимыми:
&НаКлиенте
Процедура РазвернутьВсеДерево()
КоллекцияСтрок = ДеревоНаФорме.ПолучитьЭлементы();
Для Каждого Строка Из КоллекцияСтрок Цикл
ИдентификаторСтроки = Строка.ПолучитьИдентификатор();
Элементы.ДеревоНаФорме.Развернуть(ИдентификаторСтроки, Истина); // Истина - развернуть рекурсивно
КонецЦикла;
КонецПроцедуры
В обсуждениях часто упоминается метод ОбновитьИнтерфейс(). Проанализируем ситуацию: этот метод крайне «тяжелый». Он сбрасывает все кеши клиентского приложения, заново обращается к серверу для проверки функциональных опций, прав доступа и видимости команд.
Использовать его стоит только если:
Для обычного обновления данных в дереве этот метод избыточен и приведет к существенным задержкам (фризам) в работе пользователя.
Чтобы дерево на форме гарантированно обновилось, придерживайтесь следующего чек-листа:
ЗначениеВДанныеФормы.ЭтаФорма.ОбновитьОтображениеДанных() на клиенте.Элементы.ИмяДерева.Развернуть() в цикле.Соблюдение этих правил обеспечит корректную и отзывчивую работу интерфейса ваших решений на платформе 1С.