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