Рейтинг:  5 / 5

Звезда активнаЗвезда активнаЗвезда активнаЗвезда активнаЗвезда активна
 

Привет!!! Это вторая часть практикума по работе с управляемыми формами. Напоминаю, что в качестве упражнения мы разрабатываем

документ "Смета". Т.к. этот документ подразумевает иерархичное представление табличной части, то тут надо реализовывать много

фишек, чтобы все было красиво, удобно и надежно.
В первой части мы остановились на программной работе с условным оформлением и обсчете данных в дереве. Ну что, продолжим?

 

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

по результатм которого, Вы получите эту базу. Ну или скачайте (что я не советую) из первой части DT файл версии 1.04. Вторая

цыфра в имене файла означает номер фишки уже реализованной в базе.

В первой части я анонсировал, что мы будем делать дальше. Итак, конфигуратор открыт, погнали!!!

Фишка №5
Запретим добавлять строки дерева в третий уровень. У элементов формы вида "Таблица", есть обработчик событий

"ПередНачаломДобавления". В нем в качестве второво параметра передается флаг отказа, выставив который в Итину, ничего не

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

платформы в изобилии объектов и их свойств. Все их я, естественно, не знаю. Т.е. решая данную задачу в первый раз, я не знаю

какое свойство, какого объекта проверить, чтобы выставить флаг отказа.
Давайте пройдем путь самурая, от неведенья к просветлению.
Создаем у элемента формы "ДеревоРабот" обработчик события "ДеревоРаботПередНачаломДобавления"

Сохраняемся и ставим точку останова на строке "КонецПроцедцры".

Запускаем отладку, открываем докумет. Добавляем в дерево строку, выделив строку с материалом. Именно это нам и надо запретить:

добавление строки подчиненной материалу. Точка остановы срабатывает и мы открываем табло, для изучения.
Первым шагом я в табло сую все параметры процедуры.

К сожалению в них ничего полезного. Я надеялся на параметр "Родитель", в надежде, что там будет строка дерева, но там пусто.

Видимо этот параметр актуален только для динамических списков, ведь у них тотже элемент управления "Таблица" для отображения

используется.
Ну ладно, раз в параметрах обработчика событий БОЛТ, то смотрим на наш элемент. Можно его получить по имени из коллекции

элементов, но он к нам прилетает как параметр, а лишние точки в коде - лишние.
В табло плюсиком раскрываем свойства переменной "Элемент" и читаем длинный список.
Меня заинтересовали:
ТекущаяСтрока 3 Число
ТекущиеДанные ДанныеФормыЭлементДерева ДанныеФормыЭлементДерева
ТекущаяСтрока - число, пока не очень понятно, можно ли, зная номер строки, узнать её уровень в дереве.
Раскрываем плюсиком "ТекущиеДанные". Там есть значения всех полей данной строки, а самое главное флаг
ЭтоРабота Ложь Булево

Можно смело сказать "Бинго", ведь выражение "Элемент.ТекущиеДанные.ЭтоРабота" однозначно определяет какой уровень у текущей

строки.
Однако, что если бы у нас не было этого реквизита? Ну ладно, т.к. мы учимся, то для полноты знаний, раскроем вопрос до конца.

ТекущиеДанные у нас имеют тип ДанныеФормыЭлементДерева, об этом нам говорит Табло. Вводим в синтаксис помощник эти магические

слова и видим, что у этого объекта есть не только свойства, но и методы. Читаем список методов. Меня заинтересовал метод

"ПолучитьРодителя", который в случае корневой строки возвращает "Неопределено".
И снова Бинго, мы получили второе выражение, которое не зависит от структуры данных:
Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено

Ну что, оба выражения имеют свои особенности, я выбрал второе. Также, выражение Элемент.ТекущиеДанные не всегда имеет тип

"ДанныеФормыЭлементДерева", иногда оно равно Неопределено, когда ни одна строка не выделена (например в документе вообще нет

строк, это новый документ).

Пишем следующие блоки условия:

 Если Элемент.ТекущиеДанные = Неопределено тогда
  //что делаем 1
 ИначеЕсли Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено Тогда
  //что делаем 2 
 Иначе
  //что делаем 3
 КонецЕсли;

На самом деле тут теперь у нас возникает проблема. Строки в дерево добавляются в корень только, когда дерево пустое, когда

выделена строка 1ого уровня, то добавляется второй уровень, и когда выделена строка 2ого уровня добавляется строка 3его уровня.
Тут основная раскоряка. Первый уровень можно добавить только 1 раз, а третий нам надо запретить.

Решения три: простое, дурацкое и посложнее. Простое - всегда добавлять в дерево одну корневую строчку, тем самым смещая всю

иерархию на один уровень. Вот дерево проверок для трех уровней:

 Если Элемент.ТекущиеДанные = Неопределено тогда
  //что делаем 1
 ИначеЕсли Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено Тогда
  //что делаем 2
 ИначеЕсли Элемент.ТекущиеДанные.ПолучитьРодителя().ПолучитьРодителя() = Неопределено Тогда
  //что делаем 3 
 Иначе
  //что делаем 4
 КонецЕсли;


В блоке 1 и 4 ставим Отказ = истина. Корень будем добавлять кодом сами всегда при открытии документа, а под материалами ничего не

должно быть.
И все? Да и все, это же простой путь. Бонусом у нас работают все типовые кнопки (удаления, добавления, сдвига, копирования). Не,

не все :) надо запретить трогать корень (удалять, копировать, редактировать), но это мелочи.
Минус этого пути в растрате экрана пользователя на лишнюю строку дерева и увличенный отступ слева из-за увеличевшегося уровня

вложенности данных.

Второй путь, всегда ставить отказ Истина и добавлять пустую строку кодом туда, куда мы считаем нужным. После добавления строки

надо активировать поле ввода тоже кодом. Минус такого подхода, по нажатию Esc строка не исчезнет. Далее не понятно когда какую

строку добалять. У нас три варианта: нет текущей строки, текущая строка - работа, текущая строка - материал. Нужно четко понимать

как добавить первую работу, как добавить первый материал. Можно выкрутиться так:


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

Этот путь и сложнее и не так понятен в использовании. он противоречит задумке создателей 1С в плане работы с деревом.

Третий путь закрыть вообще типовое добавление строк, спрятав команду "добавить" из интерфейса и поставив программные заглушки в

обработчики событий. И нарисовать свои две кнопки: "Добавить работу" и "добавить материал". Это немного сложнее, также надежно,

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

Я буду делать первый способ. Погнали. В реквизите формы, являющимся полем дерева, "РаботаМатериал" добавляем еще один тип данных:

строка 50.

Теперь допилим процедуру "ТабЧастьВДерево". Заменим первые её две строки на следующий код:


 ДеревоИсходное = РеквизитФормыВЗначение("ДеревоРабот");
 ДеревоИсходное.Строки.Очистить();
 Дерево = ДеревоИсходное.Строки.Добавить();
 Дерево.РаботаМатериал = "Смета";


А в конце заменим


 ЗначениеВРеквизитФормы(Дерево,"ДеревоРабот");


на


 ЗначениеВРеквизитФормы(ДеревоИсходное,"ДеревоРабот");


Конечно, теперь бы переменную "дерево" переименовать бы в "КореньДерева" для большей читабельности кода, но это не обязательно.
Далее надо закрыть доступ к корневой строке. Обработаем событие таблицы "ПередНачаломИзменения" и "ПередУдалением" вот таким

кодом:


&НаКлиенте
Процедура ДеревоРаботПередНачаломИзменения(Элемент, Отказ)
 Если Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено Тогда
  Отказ = истина;
 КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ДеревоРаботПередУдалением(Элемент, Отказ)
 Если Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено Тогда
  Отказ = истина;
 КонецЕсли;
КонецПроцедуры

Так же, нумеровать корневую строку не будем, это только сбивает глаза с толку. Для этого доработаем процедуру

"ПеренумероватьСтрокиНаКлиенте". Заменим первую строку "коллекцияРабот = ДеревоРабот.ПолучитьЭлементы();" на следующие строки:
 КорениДерева = ДеревоРабот.ПолучитьЭлементы();
 Если КорениДерева.Количество() = 0 Тогда Возврат; КонецЕсли;
 коллекцияРабот = КорениДерева[0].ПолучитьЭлементы();
Переменную назвал "КорениДерева", а не "КореньДерева", чтобы поставить акцент на том, что теоретически корня может не быть или их

может быть несколько.
Еще допилим процедуру "ПоместитьДеревоВТабЧасть", т.к. сейчас она поломатая. В начале процедуры также получим корень и уже от него будем плясать.


&НаКлиенте
Процедура ПоместитьДеревоВТабЧасть()
 Объект.Материалы.Очистить();
 КорениДерева = ДеревоРабот.ПолучитьЭлементы();
 Если КорениДерева.Количество() = 0 Тогда Возврат; КонецЕсли;
 коллекцияРабот = КорениДерева[0].ПолучитьЭлементы();
 Для Каждого стрРаботы Из коллекцияРабот Цикл
 ...
Ну и наконец, отрежем добавление строки в корень и ниже материалов.
&НаКлиенте
Процедура ДеревоРаботПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, Группа, Параметр)
 Если Элемент.ТекущиеДанные = Неопределено тогда
  Отказ = Истина;
 ИначеЕсли Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено Тогда
  
 ИначеЕсли Элемент.ТекущиеДанные.ПолучитьРодителя().ПолучитьРодителя() = Неопределено Тогда
  
 Иначе
  Отказ = Истина;
 КонецЕсли;
  
КонецПроцедуры

Может показаться, что написано коряво, 2е и 3е услови все равно надо проверить, так что этот код корректен с точки зрения

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

копирование. При копировании строка добавляется в тот же уровень, что и текущая, а значит уровни совсем другие. Нам надо

запретить копировать только корень.
Собственно, вот пример кода:


 Если Копирование Тогда
  Если Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено Тогда
   Отказ = Истина;
  КонецЕсли;
 Иначе
  Если Элемент.ТекущиеДанные = Неопределено тогда
   Отказ = Истина;
  ИначеЕсли Элемент.ТекущиеДанные.ПолучитьРодителя() = Неопределено Тогда
  
  ИначеЕсли Элемент.ТекущиеДанные.ПолучитьРодителя().ПолучитьРодителя() = Неопределено Тогда
   
  Иначе
   Отказ = Истина;
  КонецЕсли;
 КонецЕсли;

Это почти все. Не самый идеальный вариант, но редактирование дерева это тот случай, когда идеально сделать сложно :) есть

несколько подходов и в каждом есть свои нюансы. Обсуждать варианты в комментарии

Фишка 6
Дерево мы правим, но есть проблема. При выборе материала система нам предлагает выбрать сначала тип данных. Хоть мы и в строке с

материалами, но имеем право выбрать работу или написать строку.
Считаю это непреемлемым :)

Для того, чтобы порезать это безобразие воспользуемся свойством поля ввода "ДоступныеТипы". По умолчанию там все, которые есть у

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

свойство менять придется при каждом начале выбора.
Т.к. корень у нас закрыт для редактирования, то можем предположить, что его мы никогда не начнем редактировать. Остается два

варианта: работа или материал.
Вот код обработчика события:


 Если Элементы.ДеревоРабот.ТекущиеДанные = Неопределено Тогда
  СтандартнаяОбработка = Ложь;
  Возврат;
 КонецЕсли;
 Если Элементы.ДеревоРабот.ТекущиеДанные.ПолучитьРодителя().ПолучитьРодителя() = Неопределено Тогда
  Элемент.ДоступныеТипы = Новый ОписаниеТипов("СправочникСсылка.Работы");
 Иначе
  Элемент.ДоступныеТипы = Новый ОписаниеТипов("СправочникСсылка.Материалы");
 КонецЕсли;

Но это не все, еще надо в свойствах поля ввода снять флаг "ВыбиратьТип"

Теперь все красиво. Задача решена, всем спасибо, до свиданья!

П.С. нет нет, это не конец. В этом документе еще куча фишек для реализации. например запретить перетаскивание, а лучше разрешить с обработкой и контролем. Копировать работы лучше с подчиненными строками... да много чего можно придумать. Но основное я рассказал.

 

 

 

Вложения
ATTACH_DOWNLOAD_THIS_FILE_SFilSmetaV1.06.dt[ ]48 kB

Авторизуйтесь пожалуйста

Комментарии   

+1 # Сергей 09.02.2017 13:19
Спасибо огромное за продолжение!