Как преобразовать массив файлов в иерархическое ДеревоЗначений на управляемой форме 1С?

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

При работе с файловой системой в 1С часто возникает задача отобразить структуру каталогов и файлов в удобном иерархическом виде на форме. Для этих целей отлично подходит объект ДеревоЗначений. Рассмотрим подробнее, как мы можем получить список файлов и каталогов с помощью функции НайтиФайлы и затем преобразовать его в древовидную структуру, пригодную для отображения в управляемом приложении 1С:Предприятия.

Наш читатель столкнулся с ситуацией, когда стандартный массив, полученный функцией НайтиФайлы, требовалось отобразить в виде дерева на форме. Основная сложность заключалась не столько в самой логике построения дерева, сколько в правильной интеграции этого механизма с управляемыми формами, использующими интерфейс «Такси».

Задача: Получение иерархической структуры из плоского списка файлов

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

Например, для такой структуры:

C:\Temp\
├── Файл1.txt
├── Папка1\
│   ├── ФайлВПапке1.doc
│   └── Подпапка1\
│       └── ФайлВПодпапке1.xls
└── Папка2\
    └── ФайлВПапке2.pdf

Функция НайтиФайлы с параметром поиска в подкаталогах вернет массив элементов, каждый из которых будет содержать полный путь, например:

Наша цель — превратить этот список в ДеревоЗначений, которое будет выглядеть на форме как иерархическое представление файловой системы.

Шаг 1: Получаем список файлов и каталогов

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


// Определяем путь к каталогу, который будем сканировать
ПеременнаяПутьККаталогу = "C:\Temp"; 

// Используем функцию НайтиФайлы для получения списка файлов и каталогов.
// Третий параметр (Истина) означает, что поиск будет вестись и в подкаталогах.
МассивФайлов = НайтиФайлы(ПеременнаяПутьККаталогу, "*", Истина);

После выполнения этого кода переменная МассивФайлов будет содержать коллекцию объектов типа Файл, каждый из которых имеет свойство ПолноеИмя. Это свойство и будет служить основой для построения нашей иерархии.

Шаг 2: Создаем реквизит формы типа ДеревоЗначений и определяем его структуру

В управляемых формах для отображения данных в виде дерева нам потребуется реквизит формы типа ДеревоЗначений. Давайте создадим такой реквизит и определим необходимые колонки.

  1. Откроем форму, на которой мы планируем отображать дерево.
  2. В окне "Реквизиты" формы добавим новый реквизит.
  3. Укажем имя, например, ДеревоФайлов.
  4. Установим тип реквизита как ДеревоЗначений.
  5. После создания реквизита, выделим его и в окне "Свойства" добавим колонки для нашего дерева. В нашем случае нам потребуется как минимум одна колонка для отображения имени файла или каталога. Назовем ее Имя.

На стороне сервера программно, это выглядит так:


// Создаем новый объект ДеревоЗначений
ПеременноеДерево = Новый ДеревоЗначений;

// Добавляем колонку "Имя", в которой будут отображаться названия файлов и каталогов
ПеременноеДерево.Колонки.Добавить("Имя");

Шаг 3: Разрабатываем рекурсивную процедуру для заполнения ДереваЗначений

Ключевым моментом в построении иерархической структуры из плоского списка является рекурсивный подход. Мы будем брать полный путь к файлу или каталогу, разбивать его на компоненты и для каждого компонента создавать или находить соответствующую строку в ДеревеЗначений. Если у компонента пути есть вложенные части, процедура будет вызывать сама себя для обработки этих вложенных частей.

Рассмотрим процедуру ОбработатьПутьФайла, которая будет выполнять эту задачу. Данная процедура должна быть помещена в модуль формы или общий модуль с аннотацией &НаСервере, так как работа с файловой системой и заполнение ДереваЗначений происходит на сервере.


&НаСервере
// Процедура для рекурсивной обработки пути файла и заполнения ДереваЗначений
// Параметры:
//   ПолныйПуть - строка, полный путь к файлу или каталогу
//   КоллекцияСтрокДерева - коллекция строк ДереваЗначений, в которую нужно добавить элемент
Процедура ОбработатьПутьФайла(Знач ПолныйПуть, Знач КоллекцияСтрокДерева)

    // Унифицируем разделители пути, заменяя обратные слеши на прямые, если они встречаются.
    // Хотя в 1С обычно используются обратные слеши для Windows-путей, 
    // такая унификация помогает избежать ошибок при разных форматах.
    ПолныйПуть = СтрЗаменить(ПолныйПуть, "/", "\");

    // Ищем позицию первого разделителя в пути.
    ПозицияРазделителя = Найти(ПолныйПуть, "\");

    ПеременнаяИмя = "";
    ПеременнаяОстаток = "";

    Если ПозицияРазделителя = 0 Тогда
        // Если разделителя нет, значит это последний компонент пути (файл или каталог без вложенности).
        ПеременнаяИмя = ПолныйПуть;
        ПеременнаяОстаток = ""; // Оставшаяся часть пути пуста.
    Иначе
        // Если разделитель найден, выделяем текущий компонент пути (имя каталога или файла).
        ПеременнаяИмя = Лев(ПолныйПуть, ПозицияРазделителя - 1);
        // Выделяем оставшуюся часть пути для рекурсивного вызова.
        ПеременнаяОстаток = Прав(ПолныйПуть, СтрДлина(ПолныйПуть) - ПозицияРазделителя);
    КонецЕсли;

    // Ищем, существует ли уже строка с таким именем на текущем уровне дерева.
    // Это важно, чтобы не создавать дубликаты каталогов.
    СтрокаДерева = КоллекцияСтрокДерева.Найти(ПеременнаяИмя, "Имя");

    Если СтрокаДерева = Неопределено Тогда
        // Если строка с таким именем не найдена, добавляем новую строку в текущую коллекцию.
        СтрокаДерева = КоллекцияСтрокДерева.Добавить();
        // Присваиваем имя текущему элементу дерева.
        СтрокаДерева.Имя = ПеременнаяИмя;
    КонецЕсли;

    // Если после выделения текущего компонента пути осталась какая-то часть (т.е. есть вложенность),
    // вызываем эту же процедуру рекурсивно для оставшейся части пути,
    // передавая коллекцию подчиненных строк текущей строки дерева.
    Если СтрДлина(ПеременнаяОстаток) > 0 Тогда
        ОбработатьПутьФайла(ПеременнаяОстаток, СтрокаДерева.Строки);
    КонецЕсли;

КонецПроцедуры

Эта процедура позволяет нам построить иерархию, разбивая каждый полный путь на его составляющие. Каждый компонент пути становится отдельной строкой в ДеревеЗначений, и если у него есть вложенные элементы, они добавляются как подчиненные строки.

Шаг 4: Интеграция с управляемой формой и вызов заполнения на сервере

Как было верно замечено в обсуждении, одной из основных причин, почему код может не работать в управляемом приложении (интерфейс «Такси»), является контекст выполнения. Код, который напрямую взаимодействует с файловой системой (например, НайтиФайлы) и заполняет реквизиты формы, должен выполняться на сервере. После заполнения реквизит формы передается на клиент для отображения.

Рассмотрим, как мы можем вызвать нашу процедуру заполнения и передать данные в реквизит формы. Мы будем использовать обработчик события формы ПриСозданииНаСервере, чтобы дерево заполнялось при открытии формы.


// В модуле формы (в секции &НаСервере)

// Процедура-обработчик события формы "ПриСозданииНаСервере".
// Она вызывается при создании формы на сервере, до ее отображения клиенту.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Вызываем нашу процедуру для заполнения ДереваЗначений.
    // Эта процедура будет отвечать за всю логику получения файлов и построения иерархии.
    ЗаполнитьДеревоФайловНаСервере();
КонецПроцедуры

// Процедура для инициализации и заполнения реквизита формы "ДеревоФайлов".
// Эта процедура также должна выполняться на сервере.
&НаСервере
Процедура ЗаполнитьДеревоФайловНаСервере()
    // Создаем временное ДеревоЗначений.
    // Мы не работаем напрямую с реквизитом формы "ДеревоФайлов" на этом этапе,
    // так как он может быть недоступен для создания колонок или манипуляций.
    ПеременноеДерево = Новый ДеревоЗначений;
    // Добавляем колонку "Имя", как мы определили ранее.
    ПеременноеДерево.Колонки.Добавить("Имя");

    // Указываем путь к каталогу, который хотим отобразить.
    // Вы можете сделать этот путь настраиваемым, например, через реквизит формы.
    ПеременнаяПутьККаталогу = "C:\Temp"; // Например, папка Temp на диске C

    // Получаем массив объектов типа Файл из указанного каталога и всех подкаталогов.
    МассивФайлов = НайтиФайлы(ПеременнаяПутьККаталогу, "*", Истина);

    // Обходим каждый найденный файл/каталог из массива.
    Для Каждого Файл ИЗ МассивФайлов Цикл
        // Вызываем нашу рекурсивную процедуру для обработки полного имени каждого элемента
        // и добавления его в ПеременноеДерево.
        ОбработатьПутьФайла(Файл.ПолноеИмя, ПеременноеДерево.Строки);
    КонецЦикла;

    // После того как ПеременноеДерево полностью заполнено,
    // мы передаем его содержимое в реквизит формы "ДеревоФайлов".
    // Функция ЗначениеВРеквизитФормы() позволяет синхронизировать данные между серверным объектом
    // и реквизитом управляемой формы на клиенте.
    ЗначениеВРеквизитФормы(ПеременноеДерево, "ДеревоФайлов");
КонецПроцедуры

// !!! Важно: Процедура ОбработатьПутьФайла также должна быть размещена в серверном контексте,
// например, в этом же модуле формы с аннотацией &НаСервере.
// Ее код был представлен в Шаге 3.

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

Шаг 5: Размещение элемента ДереваЗначений на форме

После того как реквизит формы создан и код для его заполнения написан, нам осталось только добавить сам элемент управления на форму.

  1. Откройте форму в конфигураторе.
  2. Перейдите на вкладку "Элементы".
  3. Перетащите реквизит формы ДеревоФайлов из окна "Реквизиты" в дерево элементов формы.
  4. В свойствах созданного элемента формы "ДеревоФайлов" убедитесь, что тип отображения установлен как "Дерево" (это свойство обычно устанавливается автоматически, но стоит проверить).

Теперь при открытии формы будет вызываться процедура ПриСозданииНаСервере, которая, в свою очередь, заполнит ДеревоФайлов, и оно будет отображено пользователю в виде иерархической структуры.

Дополнительные рекомендации и отладка

Если после выполнения всех шагов дерево остается пустым или работает некорректно, рекомендуем использовать отладчик 1С. Мы можем установить точки останова в следующих местах:

  1. После вызова НайтиФайлы, чтобы убедиться, что МассивФайлов действительно содержит элементы.
  2. В начале процедуры ОбработатьПутьФайла, чтобы проверить, какие пути туда передаются.
  3. Внутри ОбработатьПутьФайла, чтобы отследить логику добавления строк в дерево.
  4. После вызова ЗначениеВРеквизитФормы, чтобы убедиться, что ПеременноеДерево содержит данные перед передачей в реквизит формы.

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

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

← На главную