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