При работе с файловой системой в 1С часто возникает задача отобразить структуру каталогов и файлов в удобном иерархическом виде на форме. Для этих целей отлично подходит объект ДеревоЗначений. Рассмотрим подробнее, как мы можем получить список файлов и каталогов с помощью функции НайтиФайлы и затем преобразовать его в древовидную структуру, пригодную для отображения в управляемом приложении 1С:Предприятия.
Наш читатель столкнулся с ситуацией, когда стандартный массив, полученный функцией НайтиФайлы, требовалось отобразить в виде дерева на форме. Основная сложность заключалась не столько в самой логике построения дерева, сколько в правильной интеграции этого механизма с управляемыми формами, использующими интерфейс «Такси».
Предположим, у нас есть каталог, в котором находятся файлы и вложенные папки, а в них, в свою очередь, могут быть другие файлы и папки. Функция НайтиФайлы может вернуть нам плоский список всех этих элементов. Нам необходимо взять этот список и преобразовать его в иерархическую структуру, где каждый каталог является узлом дерева, а файлы — его конечными элементами или листовыми узлами.
Например, для такой структуры:
C:\Temp\
├── Файл1.txt
├── Папка1\
│ ├── ФайлВПапке1.doc
│ └── Подпапка1\
│ └── ФайлВПодпапке1.xls
└── Папка2\
└── ФайлВПапке2.pdf
Функция НайтиФайлы с параметром поиска в подкаталогах вернет массив элементов, каждый из которых будет содержать полный путь, например:
C:\Temp\Файл1.txtC:\Temp\Папка1C:\Temp\Папка1\ФайлВПапке1.docC:\Temp\Папка1\Подпапка1C:\Temp\Папка1\Подпапка1\ФайлВПодпапке1.xlsC:\Temp\Папка2C:\Temp\Папка2\ФайлВПапке2.pdfНаша цель — превратить этот список в ДеревоЗначений, которое будет выглядеть на форме как иерархическое представление файловой системы.
Для начала нам необходимо получить список всех файлов и каталогов из указанного пути. Для этого мы будем использовать стандартную функцию 1С НайтиФайлы. Важным моментом является третий параметр этой функции, который отвечает за поиск во вложенных каталогах. Если установить его в Истина, функция вернет элементы из всех подкаталогов.
// Определяем путь к каталогу, который будем сканировать
ПеременнаяПутьККаталогу = "C:\Temp";
// Используем функцию НайтиФайлы для получения списка файлов и каталогов.
// Третий параметр (Истина) означает, что поиск будет вестись и в подкаталогах.
МассивФайлов = НайтиФайлы(ПеременнаяПутьККаталогу, "*", Истина);
После выполнения этого кода переменная МассивФайлов будет содержать коллекцию объектов типа Файл, каждый из которых имеет свойство ПолноеИмя. Это свойство и будет служить основой для построения нашей иерархии.
В управляемых формах для отображения данных в виде дерева нам потребуется реквизит формы типа ДеревоЗначений. Давайте создадим такой реквизит и определим необходимые колонки.
ДеревоФайлов.ДеревоЗначений.Имя.На стороне сервера программно, это выглядит так:
// Создаем новый объект ДеревоЗначений
ПеременноеДерево = Новый ДеревоЗначений;
// Добавляем колонку "Имя", в которой будут отображаться названия файлов и каталогов
ПеременноеДерево.Колонки.Добавить("Имя");
Ключевым моментом в построении иерархической структуры из плоского списка является рекурсивный подход. Мы будем брать полный путь к файлу или каталогу, разбивать его на компоненты и для каждого компонента создавать или находить соответствующую строку в ДеревеЗначений. Если у компонента пути есть вложенные части, процедура будет вызывать сама себя для обработки этих вложенных частей.
Рассмотрим процедуру ОбработатьПутьФайла, которая будет выполнять эту задачу. Данная процедура должна быть помещена в модуль формы или общий модуль с аннотацией &НаСервере, так как работа с файловой системой и заполнение ДереваЗначений происходит на сервере.
&НаСервере
// Процедура для рекурсивной обработки пути файла и заполнения ДереваЗначений
// Параметры:
// ПолныйПуть - строка, полный путь к файлу или каталогу
// КоллекцияСтрокДерева - коллекция строк ДереваЗначений, в которую нужно добавить элемент
Процедура ОбработатьПутьФайла(Знач ПолныйПуть, Знач КоллекцияСтрокДерева)
// Унифицируем разделители пути, заменяя обратные слеши на прямые, если они встречаются.
// Хотя в 1С обычно используются обратные слеши для Windows-путей,
// такая унификация помогает избежать ошибок при разных форматах.
ПолныйПуть = СтрЗаменить(ПолныйПуть, "/", "\");
// Ищем позицию первого разделителя в пути.
ПозицияРазделителя = Найти(ПолныйПуть, "\");
ПеременнаяИмя = "";
ПеременнаяОстаток = "";
Если ПозицияРазделителя = 0 Тогда
// Если разделителя нет, значит это последний компонент пути (файл или каталог без вложенности).
ПеременнаяИмя = ПолныйПуть;
ПеременнаяОстаток = ""; // Оставшаяся часть пути пуста.
Иначе
// Если разделитель найден, выделяем текущий компонент пути (имя каталога или файла).
ПеременнаяИмя = Лев(ПолныйПуть, ПозицияРазделителя - 1);
// Выделяем оставшуюся часть пути для рекурсивного вызова.
ПеременнаяОстаток = Прав(ПолныйПуть, СтрДлина(ПолныйПуть) - ПозицияРазделителя);
КонецЕсли;
// Ищем, существует ли уже строка с таким именем на текущем уровне дерева.
// Это важно, чтобы не создавать дубликаты каталогов.
СтрокаДерева = КоллекцияСтрокДерева.Найти(ПеременнаяИмя, "Имя");
Если СтрокаДерева = Неопределено Тогда
// Если строка с таким именем не найдена, добавляем новую строку в текущую коллекцию.
СтрокаДерева = КоллекцияСтрокДерева.Добавить();
// Присваиваем имя текущему элементу дерева.
СтрокаДерева.Имя = ПеременнаяИмя;
КонецЕсли;
// Если после выделения текущего компонента пути осталась какая-то часть (т.е. есть вложенность),
// вызываем эту же процедуру рекурсивно для оставшейся части пути,
// передавая коллекцию подчиненных строк текущей строки дерева.
Если СтрДлина(ПеременнаяОстаток) > 0 Тогда
ОбработатьПутьФайла(ПеременнаяОстаток, СтрокаДерева.Строки);
КонецЕсли;
КонецПроцедуры
Эта процедура позволяет нам построить иерархию, разбивая каждый полный путь на его составляющие. Каждый компонент пути становится отдельной строкой в ДеревеЗначений, и если у него есть вложенные элементы, они добавляются как подчиненные строки.
Как было верно замечено в обсуждении, одной из основных причин, почему код может не работать в управляемом приложении (интерфейс «Такси»), является контекст выполнения. Код, который напрямую взаимодействует с файловой системой (например, НайтиФайлы) и заполняет реквизиты формы, должен выполняться на сервере. После заполнения реквизит формы передается на клиент для отображения.
Рассмотрим, как мы можем вызвать нашу процедуру заполнения и передать данные в реквизит формы. Мы будем использовать обработчик события формы ПриСозданииНаСервере, чтобы дерево заполнялось при открытии формы.
// В модуле формы (в секции &НаСервере)
// Процедура-обработчик события формы "ПриСозданииНаСервере".
// Она вызывается при создании формы на сервере, до ее отображения клиенту.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Вызываем нашу процедуру для заполнения ДереваЗначений.
// Эта процедура будет отвечать за всю логику получения файлов и построения иерархии.
ЗаполнитьДеревоФайловНаСервере();
КонецПроцедуры
// Процедура для инициализации и заполнения реквизита формы "ДеревоФайлов".
// Эта процедура также должна выполняться на сервере.
&НаСервере
Процедура ЗаполнитьДеревоФайловНаСервере()
// Создаем временное ДеревоЗначений.
// Мы не работаем напрямую с реквизитом формы "ДеревоФайлов" на этом этапе,
// так как он может быть недоступен для создания колонок или манипуляций.
ПеременноеДерево = Новый ДеревоЗначений;
// Добавляем колонку "Имя", как мы определили ранее.
ПеременноеДерево.Колонки.Добавить("Имя");
// Указываем путь к каталогу, который хотим отобразить.
// Вы можете сделать этот путь настраиваемым, например, через реквизит формы.
ПеременнаяПутьККаталогу = "C:\Temp"; // Например, папка Temp на диске C
// Получаем массив объектов типа Файл из указанного каталога и всех подкаталогов.
МассивФайлов = НайтиФайлы(ПеременнаяПутьККаталогу, "*", Истина);
// Обходим каждый найденный файл/каталог из массива.
Для Каждого Файл ИЗ МассивФайлов Цикл
// Вызываем нашу рекурсивную процедуру для обработки полного имени каждого элемента
// и добавления его в ПеременноеДерево.
ОбработатьПутьФайла(Файл.ПолноеИмя, ПеременноеДерево.Строки);
КонецЦикла;
// После того как ПеременноеДерево полностью заполнено,
// мы передаем его содержимое в реквизит формы "ДеревоФайлов".
// Функция ЗначениеВРеквизитФормы() позволяет синхронизировать данные между серверным объектом
// и реквизитом управляемой формы на клиенте.
ЗначениеВРеквизитФормы(ПеременноеДерево, "ДеревоФайлов");
КонецПроцедуры
// !!! Важно: Процедура ОбработатьПутьФайла также должна быть размещена в серверном контексте,
// например, в этом же модуле формы с аннотацией &НаСервере.
// Ее код был представлен в Шаге 3.
В этом примере мы сначала создаем локальную переменную ПеременноеДерево типа ДеревоЗначений на сервере, заполняем ее всей необходимой информацией. Только после полного заполнения мы используем функцию ЗначениеВРеквизитФормы(), чтобы передать это заполненное дерево в реквизит формы ДеревоФайлов, который уже отобразится на клиенте.
После того как реквизит формы создан и код для его заполнения написан, нам осталось только добавить сам элемент управления на форму.
ДеревоФайлов из окна "Реквизиты" в дерево элементов формы.Теперь при открытии формы будет вызываться процедура ПриСозданииНаСервере, которая, в свою очередь, заполнит ДеревоФайлов, и оно будет отображено пользователю в виде иерархической структуры.
Если после выполнения всех шагов дерево остается пустым или работает некорректно, рекомендуем использовать отладчик 1С. Мы можем установить точки останова в следующих местах:
НайтиФайлы, чтобы убедиться, что МассивФайлов действительно содержит элементы.ОбработатьПутьФайла, чтобы проверить, какие пути туда передаются.ОбработатьПутьФайла, чтобы отследить логику добавления строк в дерево.ЗначениеВРеквизитФормы, чтобы убедиться, что ПеременноеДерево содержит данные перед передачей в реквизит формы.Такой пошаговый анализ позволит нам оперативно выявить, на каком этапе происходит сбой, и исправить его. Важно помнить, что отладка серверного кода в управляемом приложении требует настройки режима отладки с подключением к серверу 1С:Предприятия.
Таким образом, мы видим, что решение задачи по отображению файловой структуры в ДеревеЗначений на управляемой форме 1С состоит из нескольких взаимосвязанных шагов: правильного получения данных, рекурсивного построения иерархии и корректной интеграции с архитектурой управляемых форм.