Книга знаний

1С:Предприятие / Приемы программирования / Формы

Деструктивный анализ формы

Попытался сделать полностью виртуальную форму документа
<br>для этого записываю программно все элементы формы и их свойства.
<br>Затем пытаюсь по этому тексту нарисовать форму с "чистого листа".
<br>
<br>Проблема в том, что при чтении свойств элемента формы, я не могу никак узнать на какой странице
<br>какой панели этот элемент расположен.
<br>В свойствах элемента такой инфы нет.
Автор статьи: asady | Редакторы: Гений 1С, O-Planet, gae,
Последняя редакция №6 от 24.08.11 | История
URL: http://kb.mista.ru/article.php?id=650

Ключевые слова: Форма, копирование, элементы


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

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

От Гения 1С:
Суть деструктивного анализа - создается форма, составляем список элементов. Затем поочередно удаляем
панели и смотрим, какие элементы удалились вместе с панелью, значит они принадлежат данной панели. Я
правильно понимаю? А как учесть вложенные панели, ведь если мы случайно удалим главную панель, то и
вложенные то же удалим. Гм. Ну можно смотреть так - если в списке удаляемых элементов попалась
панель, то нужно по другому было удалять и сделать отмену.
Асади, расскажи об алгоритме, а то код нечитаемый.
Вообще на практике лучше для каждого элемента помечать к какой панели он относится, например у формы
создать метод, который будет возвращать список названий элементов и к каким панелям они относятся.
Деструктивный метод рабтает слишком долго.

От Asady 1С:

Алгоритм именно такой - гасим поочередно панели и их страницы и смотрим какие элементы форм
отзываются (1-есть, 0 - нет) на основании этого составляем сигнатуру (строка из "1" и "0") и
контрольную сумму (сумму сигнатуры).
Утверждается что сигнатура элемента формы совпадает с соответствующей сигнатурой панели/страницы.
А контрольная сумма панели/страницы - указывает уровень вложенности.

Кстати: работает алгоритм довольно шустро - по крайней мере на типовых.



Функция ДеструктивныйАнализФормы(МенеджерОбъекта, ИмяФормы) экспорт
    ТаблицаДеструктивногоАнализа=Новый ТаблицаЗначений;
    ТаблицаДеструктивногоАнализа.Колонки.Добавить("ИмяЭлемента",Новый ОписаниеТипов("Строка"));    
    ТаблицаДеструктивногоАнализа.Колонки.Добавить("ТипЭлемента");
    ТаблицаДеструктивногоАнализа.Колонки.Добавить("Сигнатура",Новый ОписаниеТипов("Строка"));
    
    ТаблицаПанелей=Новый ТаблицаЗначений;
    ТаблицаПанелей.Колонки.Добавить("Имя",Новый ОписаниеТипов("Строка"));
    ТаблицаПанелей.Колонки.Добавить("ИмяПанели",Новый ОписаниеТипов("Строка"));
    ТаблицаПанелей.Колонки.Добавить("Сигнатура",Новый ОписаниеТипов("Строка"));
    ТаблицаПанелей.Колонки.Добавить("Сумма",Новый ОписаниеТипов("Число"));
    
    СписокПанелейФормы=Новый СписокЗначений;
    
    ДеревоЭлементовФормы=Новый ДеревоЗначений;
    ДеревоЭлементовФормы.Колонки.Добавить("ИмяЭлемента");
    ДеревоЭлементовФормы.Колонки.Добавить("ПолноеИмя");
    ДеревоЭлементовФормы.Колонки.Добавить("ТипЭлемента");
    Корень=ДеревоЭлементовФормы.Строки.Добавить();
    Корень.ИмяЭлемента="ЭтаФорма";
    Корень.ПолноеИмя="ЭтаФорма";
    Корень.ТипЭлемента=Тип("Форма");
    
    
    
    
    тФорма=МенеджерОбъекта.ПолучитьФорму(ИмяФормы);
    Для каждого Элем из тФорма.ЭлементыФормы Цикл
        тТип=ТипЗнч(Элем);
        Если тТип=Тип("Панель") Тогда
            СписокПанелейФормы.Добавить(Элем.Имя);
            пан=ТаблицаПанелей.Добавить();
            пан.Имя=Элем.Имя;
            пан.ИмяПанели="";
            Для каждого Страница Из Элем.Страницы Цикл
                СписокПанелейФормы.Добавить(Элем.Имя+"_"+Страница.Имя,Элем.Имя
+"."+Страница.Имя);
                пан=ТаблицаПанелей.Добавить();
                пан.Имя=Страница.Имя;
                пан.ИмяПанели=Элем.Имя;
            КонецЦикла;    
        Иначе    
            стр=ТаблицаДеструктивногоАнализа.Добавить();
            стр.ИмяЭлемента=Элем.Имя;
            стр.ТипЭлемента=тТип;
        КонецЕсли;    
    КонецЦикла;    
    Для каждого пан из СписокПанелейФормы Цикл
        ТаблицаДеструктивногоАнализа.Колонки.Добавить(пан.Значение,Новый 
ОписаниеТипов("Число"));    
        ТаблицаПанелей.Колонки.Добавить(пан.Значение,Новый ОписаниеТипов("Число"));    
    КонецЦикла;        
    Для каждого пан из СписокПанелейФормы Цикл
        тФорма=Неопределено;
        тФорма=МенеджерОбъекта.ПолучитьФорму(ИмяФормы);
        тЭлементыФормы=тФорма.ЭлементыФормы;
        тПоз=Найти(пан.Представление,".");
        Если тПоз>0 Тогда // это страница панели
            тИмяПанели=Лев(пан.Значение,тПоз-1);
            тИмяСтраницы=Сред(пан.Значение,тПоз+1);
            фПанель=тЭлементыФормы[тИмяПанели];
            фСтраница=фПанель.Страницы.Найти(тИмяСтраницы);
            Если фСтраница<>неопределено Тогда
                фПанель.Страницы.Удалить(фСтраница);    
            КонецЕсли;    
        Иначе    
            тЭлементыФормы.Удалить(тЭлементыФормы[пан.Значение]);
        КонецЕсли;    
        ТаблицаДеструктивногоАнализа.ЗаполнитьЗначения(0,пан.Значение);
        ТаблицаПанелей.ЗаполнитьЗначения(0,пан.Значение);
        Для каждого Элем из ТаблицаДеструктивногоАнализа Цикл
            Попытка
                тЭлем=тЭлементыФормы[Элем.ИмяЭлемента];
            Исключение
                Элем[пан.Значение]=1;
            КонецПопытки;    
        КонецЦикла;                
        Для каждого Элем из ТаблицаПанелей Цикл
            тИмяЭлемента=Элем.Имя;
            ЭтоСтраница=Ложь;
            Если Элем.ИмяПанели<>"" Тогда // это страница панели
                ЭтоСтраница=Истина;
                тИмяЭлемента=Элем.ИмяПанели+"_"+Элем.Имя;
                Если пан.Значение=Элем.ИмяПанели Тогда
                    Элем[пан.Значение]=1;    
                    Продолжить;
                КонецЕсли;    
            КонецЕсли;    
            Если пан.Значение=тИмяЭлемента Тогда
                Элем[пан.Значение]=1;    
                Продолжить;
            КонецЕсли;    
            тПоз=Найти(пан.Представление,".");
            Если тПоз>0 Тогда // это страница панели
                тИмяПанели=Лев(пан.Значение,тПоз-1);
                Если тИмяПанели=Элем.Имя Тогда
                    Продолжить;
                КонецЕсли;        
            КонецЕсли;    
            
            Если ЭтоСтраница Тогда // это страница панели
                Попытка    
                    фПанель=тЭлементыФормы[Элем.ИмяПанели];
                    фСтраница=фПанель.Страницы.Найти(Элем.Имя);
                Исключение
                    фСтраница=неопределено;
                КонецПопытки;            
                Если фСтраница=неопределено Тогда
                    Элем[пан.Значение]=1;    
                КонецЕсли;    
            Иначе    
                Попытка
                    тЭлем=тЭлементыФормы[Элем.Имя];
                Исключение
                    Элем[пан.Значение]=1;
                КонецПопытки;    
            КонецЕсли;    
        КонецЦикла;                
    КонецЦикла;
    колстр=ТаблицаДеструктивногоАнализа.Количество();
    Для СчСтр=1 по колстр Цикл
        текСтр=ТаблицаДеструктивногоАнализа[колстр-СчСтр];
        тСигнатура="";
        тСумма=0;
        Для каждого пан из СписокПанелейФормы Цикл
            тСигнатура=тСигнатура+текСтр[пан.Значение];
            тСумма=тСумма+текСтр[пан.Значение];
        Конеццикла;            
        текСтр.Сигнатура=тСигнатура;
        Если тСумма=0 Тогда
            тЭлемент=Корень.Строки.Добавить();
            тЭлемент.ИмяЭлемента=текСтр.ИмяЭлемента;
            тЭлемент.ПолноеИмя=текСтр.ИмяЭлемента;
            тЭлемент.ТипЭлемента=текСтр.ТипЭлемента;
            ТаблицаДеструктивногоАнализа.Удалить(текСтр);
        КонецЕсли;    
    Конеццикла;    
    
    Для каждого стр из ТаблицаПанелей Цикл
        тСигнатура="";
        тСумма=0;
        Для каждого пан из СписокПанелейФормы Цикл
            тСигнатура=тСигнатура+стр[пан.Значение];
            тСумма=тСумма+стр[пан.Значение];
        Конеццикла;            
        стр.Сигнатура=тСигнатура;
        стр.Сумма=тСумма;
    Конеццикла;    
    ТаблицаПанелей.Сортировать("Сумма");
    Для каждого стр из ТаблицаПанелей Цикл
        тДлинаСигнатуры=СтрДлина(стр.Сигнатура);
        тСигнатура=стр.Сигнатура;
        тСумма=стр.Сумма;
        тРодитель=Корень;
        Если тСумма>1 Тогда
            Для тПоз=1 по тДлинаСигнатуры Цикл
                тФлаг=Сред(тСигнатура,тПоз,1);
                Если тФлаг="0" Тогда
                    Продолжить;
                КонецЕсли;
                тИмяПанелиРодителя=ТаблицаПанелей.Колонки[3+тПоз].Имя;
                Если стр.ИмяПанели="" Тогда
                    тИмяСтроки=стр.Имя;
                Иначе
                    тИмяСтроки=стр.ИмяПанели+"_"+стр.Имя;
                КонецЕсли;    
                Если тИмяСтроки=тИмяПанелиРодителя Тогда
                    Продолжить;
                КонецЕсли;    
                
мРодитель=Корень.Строки.Найти(тИмяПанелиРодителя,"ПолноеИмя",Истина);
                Если мРодитель<>неопределено Тогда
                    Если мРодитель.Уровень()>тРодитель.Уровень() Тогда
                        тРодитель=мРодитель;
                    КонецЕсли;            
                КонецЕсли;    
            КонецЦикла;    
        КонецЕсли;    
        тПанель=тРодитель.Строки.Добавить();
        тПанель.ИмяЭлемента=стр.Имя;
        тПанель.ПолноеИмя=стр.Имя;
        тПанель.ТипЭлемента=Тип("Панель"); //!!!! добавили панель в дерево
        Если стр.ИмяПанели<>"" Тогда
            тПанель.ТипЭлемента=Тип("СтраницаПанели");
            тПанель.ПолноеИмя=стр.ИмяПанели+"_"+стр.Имя;
        КонецЕсли;    
        
    КонецЦикла;            
    
    Для каждого Элем из ТаблицаДеструктивногоАнализа Цикл
        сСигнатура=Элем.Сигнатура;
        эПанель=ТаблицаПанелей.Найти(сСигнатура,"Сигнатура");
        тРодитель=Корень;
        Если эПанель<>неопределено Тогда
            ИмяСтрокиДерева=эПанель.Имя;
            Если эПанель.ИмяПанели<>"" Тогда
                ИмяСтрокиДерева=эПанель.ИмяПанели+"_"+эПанель.Имя;
            КонецЕсли;        
            эСтрока=Корень.Строки.Найти(ИмяСтрокиДерева,"ПолноеИмя",Истина);
            Если эСтрока<>неопределено Тогда
                тРодитель=эСтрока;
            Конецесли;    
        КонецЕсли;        
        тЭлемент=тРодитель.Строки.Добавить();
        тЭлемент.ИмяЭлемента=Элем.ИмяЭлемента;
        тЭлемент.ПолноеИмя=Элем.ИмяЭлемента;
        тЭлемент.ТипЭлемента=Элем.ТипЭлемента;
    КонецЦикла;    

    возврат ДеревоЭлементовФормы;
КонецФункции    




На данный код вдохновили:
Гений1С
Книга знаний: v8: Копировальщик форм - вложенные формы
и
TormozIT
Книга знаний: v8: Настройка формы по макету

пользуйтесь коллеги.

От O-Planet

Ну вы <ВЦ> и писатели... Не лень столько пИсать? :D
Вот как этот метод сделал бы мастер... вроде меня... и такой же скромный :D


// Основная функция. На выходе получаем дерево эл-тов формы, распределенных по панелям
// На вход подаем элементы какой-то формы. Хоть текущей, потому как ЗНАЧ
Функция СтроитьДеревоЭлементовФормы(Знач Элементы)
    ТабЭлтов=АнализаАднака(Элементы);
    ТабЭлтов.Сортировать("Уровень Возр, ЭтоПанель Возр");
    Береза=Новый ДеревоЗначений;
    Береза.Колонки.Добавить("ЭлементФормы");
    ВзраститьБерезу(Береза.Строки,ТабЭлтов,0);
    Возврат Береза;
КонецФункции    

// Тут строим таблицу всех элементов и раскидываем их по панелям
Функция АнализаАднака(Элементы)
    Панели=Новый Массив;
    ЭлТы=Новый ТаблицаЗначений;
    ЭлТы.Колонки.Добавить("Элт");
    ЭлТы.Колонки.Добавить("Панель");
    ЭлТы.Колонки.Добавить("Уровень");
    ЭлТы.Колонки.Добавить("ЭтоПанель");
    Для Каждого Эл Из Элементы Цикл
        Нов=Элты.Добавить();
        Нов.Элт=Эл;
        Нов.Уровень=0;
        Нов.ЭтоПанель=Ложь;
        Если ТипЗнч(Эл)=Тип("Панель") Тогда
            Панели.Добавить(Эл);
            Нов.ЭтоПанель=Истина;
        КонецЕсли;    
    КонецЦикла;
    Для К=0 По Панели.ВГраница() Цикл
        ОбьрабатываемПанель(Элементы,Панели[К],ЭлТы);
    КонецЦикла;    
    Возврат ЭлТы;
КонецФункции    

// А тут удаляем очередную панель и смотрим, что осталось
Процедура ОбьрабатываемПанель(Знач Элементы,Панель,ЭлТы)
    Элементы.Удалить(Панель);
    Для Каждого Стр Из ЭлТы Цикл
        Если Стр.Элт<>Панель Тогда
            Если Элементы.Найти(Стр.Элт.Имя)=Неопределено Тогда
                Стр.Панель=Панель;
                Стр.Уровень=Стр.Уровень+1;
            КонецЕсли;    
        КонецЕсли;    
    КонецЦикла;    
КонецПроцедуры    

// Ну это типа преобразование таблицы значений в дерево на некотором уровне. 
// Рекурсия!!! И этим все 
сказано...
Процедура ВзраститьБерезу(ВеткиБерезы,ТабЭлтов,Уровень)
    Для Каждого Стр Из ТабЭлтов Цикл
        Если Стр.Уровень<>Уровень Тогда
            Продолжить;
        КонецЕсли;    
        НоваяВетка=ВеткиБерезы.Добавить();
        НоваяВетка.ЭлементФормы=Стр.Элт;
        Если Стр.ЭтоПанель Тогда
            ВзраститьБерезу(НоваяВетка.Строки,ТабЭлтов,Уровень+1);
        КонецЕсли;    
    КонецЦикла;    
КонецПроцедуры    


Теперь вот можно пользоваться...

Всем успехов.

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика