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