Как программно развернуть динамический список в виде дерева после установки отбора?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с управляемыми формами в платформе «1С:Предприятие 8.3» разработчики часто используют динамический список с настроенными группировками для отображения данных в виде иерархического дерева. Типичная задача — при активизации строки в одной таблице автоматически устанавливать отбор в динамическом списке и разворачивать полученные строки группировок, чтобы пользователь сразу видел детальные данные.

Однако при попытке вызвать стандартный метод Развернуть() сразу после изменения отбора разработчики сталкиваются с тем, что дерево остается свернутым, а код визуально не выполняет никаких действий. В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем внутренние механизмы платформы и рассмотрим надежное решение с использованием обработчика ожидания.

Выясняем причину: почему метод Развернуть() не работает сразу?

Для начала проанализируем, как ведет себя динамический список при программном изменении его параметров или отборов. Основная особенность динамического списка заключается в его асинхронном поведении. Когда мы выполняем код изменения отбора, платформа не отправляет запрос к базе данных мгновенно в той же строчке кода. Она лишь фиксирует изменения в настройках компоновки данных.

Фактическое получение данных и обновление интерфейса происходят чуть позже — когда управление возвращается платформе (после завершения текущей процедуры на клиенте). Посмотрим на проблемный код, который часто пишут разработчики:


&НаКлиенте
Процедура ПланограммыПриАктивизацииСтроки(Элемент)
    
    Если Элемент.ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Планограмма = Элемент.ТекущиеДанные.Планограмма;
    
    Если ЗначениеЗаполнено(Планограмма) Тогда
        // Устанавливаем новый отбор в динамическом списке
        ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка(ВыкладкиМагазины, "Планограмма", Планограмма, Истина, ВидСравненияКомпоновкиДанных.Равно);
        
        // Попытка сразу развернуть текущую строку группировки
        Если Элементы.ВыкладкиМагазины.ТекущаяСтрока <> Неопределено Тогда
            Элементы.ВыкладкиМагазины.Развернуть(Элементы.ВыкладкиМагазины.ТекущаяСтрока, Истина);
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

В данном примере метод Развернуть() вызывается сразу после изменения отбора. В этот момент на клиенте еще нет обновленных данных, соответствующих новому отбору. Идентификатор строки, хранящийся в свойстве ТекущаяСтрока, имеет тип СтрокаГруппировкиДинамическогоСписка. Поскольку данные списка находятся в процессе обновления (пересчета), платформа попросту «не видит» эту строку в актуальном контексте отображения и игнорирует вызов метода.

Решение проблемы с помощью обработчика ожидания

Чтобы обойти ограничение асинхронности динамического списка, нам необходимо искусственно «разорвать» выполнение кода и дать платформе время на обновление данных. Лучший инструмент для этого — использование временного однократного обработчика ожидания.

Рассмотрим по шагам, как правильно реализовать этот механизм:

  1. При изменении отбора вместо мгновенного вызова метода Развернуть() мы подключаем обработчик ожидания с минимальной задержкой (например, 0.1 секунды).
  2. Передаем управление платформе, чтобы она успела отправить запрос к базе данных, получить новые строки динамического списка и обновить форму.
  3. В процедуре-обработчике ожидания выполняем позиционирование и программное развертывание нужного узла дерева.

Посмотрим на пример практической реализации данного подхода:


&НаКлиенте
Процедура ПланограммыПриАктивизацииСтроки(Элемент)
    
    Если Элемент.ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Планограмма = Элемент.ТекущиеДанные.Планограмма;
    
    Если ЗначениеЗаполнено(Планограмма) Тогда
        // Изменяем отбор динамического списка
        ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка(ВыкладкиМагазины, "Планограмма", Планограмма, Истина, ВидСравненияКомпоновкиДанных.Равно);
        
        // Подключаем однократный обработчик ожидания с задержкой 0.1 секунды
        ПодключитьОбработчикОжидания("РазвернутьТекущуюГруппировкуСписка", 0.1, Истина);
    КонецЕсли;
    
КонецПроцедуры

&НаКлиенте
Процедура РазвернутьТекущуюГруппировкуСписка()
    
    // Проверяем, определена ли текущая строка после обновления данных
    Если Элементы.ВыкладкиМагазины.ТекущаяСтрока <> Неопределено Тогда
        // Программно разворачиваем узел дерева динамического списка
        Элементы.ВыкладкиМагазины.Развернуть(Элементы.ВыкладкиМагазины.ТекущаяСтрока, Истина);
    КонецЕсли;
    
КонецПроцедуры

В методе ПодключитьОбработчикОжидания() третьим параметром мы передаем значение Истина. Это указывает платформе, что обработчик должен выполниться строго один раз, избавляя нас от необходимости вручную вызывать метод ОтключитьОбработчикОжидания() внутри самой процедуры.

Особенности идентификации строк и альтернативные методы

При работе с группировками динамического списка важно учитывать несколько технических нюансов, которые могут повлиять на стабильность работы интерфейса:

1. Специфика типа данных группировки. Идентификатор строки группировки имеет тип СтрокаГруппировкиДинамическогоСписка. Этот идентификатор формируется платформой динамически на основании значений полей, по которым выполнена группировка. Если в результате наложения нового отбора состав данных или ключевые поля изменились, старые идентификаторы строк перестают существовать. Именно поэтому важно считывать свойство ТекущаяСтрока непосредственно внутри обработчика ожидания, а не передавать сохраненное ранее значение.

2. Метод «ПоказатьВсеГруппировки». Если вашей задачей является полное раскрытие всех ветвей дерева динамического списка после установки отбора, можно воспользоваться альтернативным методом расширения формы таблицы — ПоказатьВсеГруппировки(). Он также чувствителен к асинхронности, поэтому его запуск тоже рекомендуется выполнять через описанный выше обработчик ожидания:


&НаКлиенте
Процедура РазвернутьВсеГруппировкиСписка()
    // Разворачивает абсолютно все доступные узлы дерева
    Элементы.ВыкладкиМагазины.ПоказатьВсеГруппировки();
КонецПроцедуры

3. Поведение на новых версиях платформы (8.3.20 и выше). В современных релизах платформы механизмы фонового получения данных были существенно оптимизированы. В некоторых случаях при работе с большими объемами информации или при медленном сетевом соединении задержки в 0.1 секунды может не хватить для физического получения данных с сервера. Для обеспечения максимальной отказоустойчивости в обработчике ожидания можно реализовать дополнительную проверку: если строка все еще не определена, мы можем повторно подключить обработчик ожидания еще на один короткий такт.

4. Производительность интерфейса. Помните, что принудительное программное раскрытие большого количества ветвей дерева на клиенте заставляет платформу порциями догружать данные для отображения всех подчиненных элементов. Если ваше дерево содержит тысячи элементов и глубокую иерархию, операция раскрытия может вызвать кратковременное зависание интерфейса. Старайтесь ограничивать глубину раскрытия или использовать предварительные жесткие отборы.

← На главную