8.2 УП
Необходимо реализовать трехпозиционный флажок в ДеревеЗначений на управляемой форме 1С предприятия 8.2 , который работает по следующему алгоритму:
* Если у элемента ДереваЗначений нет подчиненных элементов, то флажок может быть либо “включен”, либо “выключен”.
* Если у элемента есть подчиненные, то флажок “включен”, если ВКЛЮЧЕНЫ флажки у ВСЕХ подчиненных элементов, “выключен” если ВЫКЛЮЧЕНЫ флажки у ВСЕХ подчиненных элементов.
Если у некоторых подчиненных элементов флажки включены, а у некоторых выключены, то флажок родительского элемента имеет значение “неопределенно”.
* При включении флажка у родительского элемента, включаются флажки ВСЕХ подчиненных, при выключении флажка у родительского элемента, выключаются флажки ВСЕХ подчиненных.
Возьмем из первой статьи процедуру построения дерева значений и доработаем ее, добавив в дерево еще одну колонку, которая будет содержать элемент управления флажок и реализуем для него обработчик события “ПриИзменении”. Весь алгоритм сводится к обработке изменения флажка: у всех подчиненных элементов необходимо включить/выключить флажки в зависимости от состояния флажка текущего элемента, а у всех родителей текущего элемента (которые расположены на верхних уровнях иерархии) проставить флажки в состояния “включен”/”выключен”/”неопределенно” в соответствии с вышеописанным алгоритмом. Пример -
Скачивать файлы может только зарегистрированный пользователь!
Код 1C v 8.2 УП &НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Создание и заполнение "обычного" объекта прикладного типа ДеревоЗначений,
// который будет отображен на управляемой форме
ДеревоОбъект = Новый ДеревоЗначений;
ДеревоОбъект.Колонки.Добавить("Узел", Новый ОписаниеТипов("Строка"));
ДеревоОбъект.Колонки.Добавить("Пометка", Новый ОписаниеТипов("Число"));
Для к1 = 1 По 3 Цикл
СтрокаУ1 = ДеревоОбъект.Строки.Добавить();
СтрокаУ1.Узел = "Узел"+к1;
Для к2 = 1 По 3 Цикл
СтрокаУ2 = СтрокаУ1.Строки.Добавить();
СтрокаУ2.Узел = СтрокаУ1.Узел+к2;
Для к3 = 1 По 3 Цикл
СтрокаУ3 = СтрокаУ2.Строки.Добавить();
СтрокаУ3.Узел = СтрокаУ2.Узел+к3;
КонецЦикла;
КонецЦикла;
КонецЦикла;
// Создание Реквизита формы типа ДанныеФормыДерево
МассивДобавляемыхРеквизитов = Новый Массив;
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Дерево",
Новый ОписаниеТипов("ДеревоЗначений")));
Для Каждого Колонка Из ДеревоОбъект.Колонки Цикл
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(Колонка.Имя,
Колонка.ТипЗначения, "Дерево"));
КонецЦикла;
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);
// Преобразование объекта прикладного типа ДеревоЗначений
// в реквизит управляемой формы (данные формы)
ЗначениеВРеквизитФормы(ДеревоОбъект, "Дерево");
// Создание элемента формы типа ТаблицаФормы для отображения дерева
ЭлементДерево = Элементы.Добавить("Дерево", Тип("ТаблицаФормы"));
ЭлементДерево.ПутьКДанным = "Дерево";
ЭлементДерево.Отображение = ОтображениеТаблицы.Дерево;
Для Каждого Колонка Из ДеревоОбъект.Колонки Цикл
НовыйЭлемент = Элементы.Добавить(Колонка.Имя, Тип("ПолеФормы"),
ЭлементДерево);
НовыйЭлемент.ПутьКДанным = "Дерево." + Колонка.Имя;
Если НЕ Колонка.Имя = "Пометка" Тогда
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
Иначе
НовыйЭлемент.Вид = ВидПоляФормы.ПолеФлажка;
НовыйЭлемент.ТриСостояния = Истина;
НовыйЭлемент.УстановитьДействие("ПриИзменении",
"ФлажокПриИзменении");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура ФлажокПриИзменении(Элемент)
ИДТекущейСтроки = Элементы["Дерево"].ТекущаяСтрока;
Если ИДТекущейСтроки <> Неопределено Тогда
ЭлементКоллекции = ЭтаФорма["Дерево"].НайтиПоИдентификатору(
ИДТекущейСтроки);
Если ЭлементКоллекции.Пометка = 2 Тогда
ЭлементКоллекции.Пометка = 0;
КонецЕсли;
УстановкаФлажков(ЭлементКоллекции, ЭлементКоллекции.Пометка);
Родитель = ЭлементКоллекции.ПолучитьРодителя();
Пока Родитель <> Неопределено Цикл
Родитель.Пометка = ?(УстановленноДляВсех(ЭлементКоллекции),
ЭлементКоллекции.Пометка, 2);
ЭлементКоллекции = Родитель;
Родитель = ЭлементКоллекции.ПолучитьРодителя();
КонецЦикла;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура УстановкаФлажков(ЭлементКоллекции, ЗначениеПометки)
ПодчинЭлементы = ЭлементКоллекции.ПолучитьЭлементы();
Для Каждого ТекЭлемент Из ПодчинЭлементы Цикл
ТекЭлемент.Пометка = ЗначениеПометки;
УстановкаФлажков(ТекЭлемент, ТекЭлемент.Пометка);
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Функция УстановленноДляВсех(ЭлементКоллекции)
СоседниеЭлементы =
ЭлементКоллекции.ПолучитьРодителя().ПолучитьЭлементы();
Для Каждого ТекЭлемент Из СоседниеЭлементы Цикл
Если ТекЭлемент.Пометка <> ЭлементКоллекции.Пометка Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
8.х
На форме имеется табличное поле, источником данных для которого является дерево значений. В одной из колонок расположен элемент управления «Флажок». Необходимо что бы он мог принимать три состояния «Включен», «Выключен» и «Неопределено». При этом состояние «Неопределено» или так называемый «серый флажок», т.е. выбранный частично, устанавливается в том случае, если не у всех подчиненных строк состояние флажка одинаковое. Так же если строка имеет подчиненные строки, то при изменении её флажка, соответствующим образом должны изменяться флажки и всех подчиненных строк.
Во-первых, добавим нашу колонку с флажком в дерево значений:
Код 1C v 8.х Дерево.Колонки.Добавить("Пометка",Новый ОписаниеТипов("Число"));
ЭлементыФормы.Дерево.СоздатьКолонки();
КолонкиДерева=ЭлементыФормы.Дерево.Колонки;
КолонкиДерева.Пометка.ДанныеФлажка="Пометка";
КолонкиДерева.Пометка.УстановитьЭлементУправления(Тип("Флажок"));
КолонкиДерева.Пометка.РежимРедактирования=РежимРедактированияКолонки.Непосредственно;
КолонкиДерева.Пометка.ТриСостоянияФлажка=Истина;
Во-вторых, при изменении флажка нужно устанавливать соответствующие значения в подчиненных строках и контролировать значение флажка в «строке-родителе»:
Код 1C v 8.х Процедура ДеревоПриИзмененииФлажка(Элемент, Колонка)
ТекущаяСтрока=Элемент.ТекущаяСтрока;
Если ТекущаяСтрока.Пометка=2 Тогда
ТекущаяСтрока.Пометка=0;
КонецЕсли;
УстановкаФлажков(ТекущаяСтрока,ТекущаяСтрока.Пометка);
Пока ТекущаяСтрока.Родитель<>Неопределено Цикл
ТекущаяСтрока.Родитель.Пометка=?(УстановленноДляВсех(ТекущаяСтрока),ТекущаяСтрока.Пометка,2);
ТекущаяСтрока=ТекущаяСтрока.Родитель;
КонецЦикла;
КонецПроцедуры
Здесь процедура УстановкаФлажков() рекурсивно устанавливает флажки в подчиненных строках:
Код 1C v 8.х Процедура УстановкаФлажков(ТекущаяСтрока,Значение)
Для Каждого Стр Из ТекущаяСтрока.Строки Цикл
Стр.Пометка=Значение;
УстановкаФлажков(Стр,Стр.Пометка);
КонецЦикла;
КонецПроцедуры
А функция УстановленноДляВсех() проверяет установлено ли одинаковое значение для всех строк на текущем уровне:
Код 1C v 8.х Функция УстановленноДляВсех(Строка)
Для Каждого Стр Из Строка.Родитель.Строки Цикл
Если Стр.Пометка<>Строка.Пометка Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
Хочу поделиться с посетителями сайта своим подходом к преобразованию таблицы значений в дерево значений и обратно.
Вообще, при разработке отраслевой задачи, была необходимость почти во всех документах, выводить информацию в виде дерева и хранить ее в табличных частях документа, а также в интерактивной обработке данных в виде дерева.
Отсюда появился небольшой модуль для расширения возможности работы с деревом значений, хотя и с некоторыми оговорками.
Основной идеей является использование двух ключевых реквизитов/колонок КлючСтроки и КлючСвязи.
Однако они не всегда необходимы. Код, на мой взгляд достаточно "высушен".
Код 1C v 8.х ////////////////////////////////////////////////////////////////////////////////
// ПРОЦЕДУРЫ И ФУНКЦИИ ДЛЯ РАБОТЫ С ДЕРЕВОМ ЗНАЧЕНИЙ
// Функция формирует значение нового ключа строки табличной части.
//
// Параметры:
// Дерево - дерево значений
//
Функция ПолучитьНовыйКлючСтрокиДерева(Дерево, СписокКлючей = Неопределено) Экспорт
Если СписокКлючей = Неопределено Тогда
СписокКлючей = Новый СписокЗначений;
СписокКлючей.Добавить(0);
КонецЕсли;
Для Каждого СтрокаДерева Из Дерево.Строки Цикл
СписокКлючей.Добавить(СтрокаДерева.КлючСтроки);
ПолучитьНовыйКлючСтрокиДерева(СтрокаДерева, СписокКлючей);
СписокКлючей.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
МаксКлюч = СписокКлючей[0].Значение + 1;
КонецЦикла;
Возврат МаксКлюч;
КонецФункции // ПолучитьНовыйКлючСтрокиДерева()
// Процедура обновляет ключи связи в дереве значений
//
Процедура ОбновитьКлючиСвязиВДеревеЗначений(Дерево) Экспорт
Для Каждого СтрокаДерева Из Дерево.Строки Цикл
Попытка
СтрокаДерева.КлючСвязи = СтрокаДерева.Родитель.КлючСтроки;
Исключение
СтрокаДерева.КлючСвязи = 0;
КонецПопытки;
ОбновитьКлючиСвязиВДеревеЗначений(СтрокаДерева);
КонецЦикла;
КонецПроцедуры // ОбновитьКлючиСвязиВДеревеЗначений()
// Процедура обновляет ключи связи в дереве значений
//
Процедура ОбновитьКлючиСтрокВДеревеЗначений(Дерево, КлючСтроки = 1) Экспорт
Для Каждого СтрокаДерева Из Дерево.Строки Цикл
СтрокаДерева.КлючСтроки = КлючСтроки;
КлючСтроки = КлючСтроки + 1;
ОбновитьКлючиСтрокВДеревеЗначений(СтрокаДерева, КлючСтроки);
КонецЦикла;
КонецПроцедуры // ОбновитьКлючиСвязиВДеревеЗначений()
// Процедура выгружает данные из дерева значений в таблицу значений
// данные выгружаются только в таблицу со сходным набором реквизитов
//
Функция ВыгрузитьДеревоЗначенийВТаблицуЗначений(Дерево, Таблица = Неопределено) Экспорт
Если Таблица = Неопределено Тогда
Таблица = Новый ТаблицаЗначений;
Для Каждого Колонка Из Дерево.Колонки Цикл
Таблица.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
КонецЦикла;
КонецЕсли;
Для Каждого СтрокаДерева Из Дерево.Строки Цикл
ЗаполнитьЗначенияСвойств(Таблица.Добавить(), СтрокаДерева);
ВыгрузитьДеревоЗначенийВТаблицуЗначений(СтрокаДерева, Таблица);
КонецЦикла;
Возврат Таблица;
КонецФункции //ВыгрузитьДеревоЗначенийВТаблицуЗначений()
// Процедура выгружает данные из таблицы значений в дерево значений
// данные выгружаются только в таблицу со сходным набором реквизитов
//
// Параметры:
//
// КлючСтроки - имя колонки ТаблицыЗначений - уникальный идентификатор
// КлючСвязи - имя колонки ТаблицыЗначений - указатель привязки к строке Дерева,
// своего рода указатель на "Родителя"
//
Функция ВыгрузитьТаблицуЗначенийВДеревоЗначений(Таблица, КлючСтроки = "КлючСтроки", КлючСвязи = "КлючСвязи") Экспорт
Дерево = Новый ДеревоЗначений;
Для Каждого Колонка Из Таблица.Колонки Цикл
Дерево.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
КонецЦикла;
Для Каждого СтрокаТаблицы Из Таблица Цикл
СтрокаГруппировки = Дерево.Строки.Найти(СтрокаТаблицы[КлючСвязи], КлючСтроки,Истина);
Если СтрокаГруппировки = Неопределено Тогда
ЗаполнитьЗначенияСвойств(Дерево.Строки.Добавить(), СтрокаТаблицы);
Иначе
ЗаполнитьЗначенияСвойств(СтрокаГруппировки.Строки.Добавить(), СтрокаТаблицы);
КонецЕсли;
КонецЦикла;
Возврат Дерево;
КонецФункции //ВыгрузитьТаблицуЗначенийВДеревоЗначений()
// Процедура устанавливает значение во всем дереве значений
//
Процедура УстановитьЗначениеКолонкиДерева(Дерево, Колонка, Значение) Экспорт
Для каждого СтрокаДерева Из Дерево.Строки Цикл
СтрокаДерева[Колонка] = Значение;
УстановитьЗначениеКолонкиДерева(СтрокаДерева, Колонка, Значение);
КонецЦикла;
КонецПроцедуры //УстановитьЗначениеКолонкиДерева()
// Процедура копирует подчиненные строки дерева значений
//
Процедура СкопироватьПодчиненныеСтроки(СтрокаПриемник, СтрокаИсточник)
Для каждого Строка Из СтрокаИсточник.Строки Цикл
НоваяСтрока = СтрокаПриемник.Строки.Добавить();
НоваяСтрока.КлючСвязи = СтрокаПриемник.КлючСтроки;
ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
СкопироватьПодчиненныеСтроки(НоваяСтрока, Строка);
КонецЦикла;
КонецПроцедуры // СкопироватьПодчиненныеСтроки()
// Процедура переносит выделенные строки дерева значений в указанную ветку
//
Процедура ПеренестиСтрокиДереваЗначений(СтрокаПриемник, ВыделенныеСтроки) Экспорт
Если НЕ СтрокаПриемник = Неопределено Тогда
МассивСтрок = Новый Массив;
Для Каждого СтрокаПереноса Из ВыделенныеСтроки Цикл
МассивСтрок.Добавить(СтрокаПереноса);
НоваяСтрока = СтрокаПриемник.Строки.Добавить();
СкопироватьПодчиненныеСтроки(НоваяСтрока, СтрокаПереноса);
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаПереноса);
НоваяСтрока.КлючСвязи = СтрокаПриемник.КлючСтроки;
КонецЦикла;
Для Каждого СтрокаДерева Из МассивСтрок Цикл
Если СтрокаДерева.Родитель = Неопределено Тогда
СтрокаДерева.Строки.Удалить(СтрокаДерева);
Иначе
СтрокаДерева.Родитель.Строки.Удалить(СтрокаДерева);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры //ПеренестиСтрокиДереваЗначений()
Александр Синцов (Sintson)