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