Книга знаний

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

v8: Настройка формы по макету

В случае, если вам необходимо изменить что то в диалоге типовой формы, не торопитесь изменять его. Ведь можно использовать динамическую модификацию формы перед открытием.Автор статьи: TormozIT | Редакторы: Гений 1С
Последняя редакция №24 от 21.02.07 | История
URL: http://kb.mista.ru/article.php?id=327

Ключевые слова: макет, форма, настройка, динамическая


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

Но есть и еще вариант. Нужно создать копию формы в конфигураторе, дав ей имя <Префикс>+<ОригинальноеИмя>+<Макет> и сделать там все необходимые изменения диалога в конфигураторе.
В разделе основной программы макета нужно заполнить список произведенных настроек. В значение элемента списка следует помещать сам элемент формы, а в представление строку настроек для измененных элементов (подробнее в описании функции ЛксНастроитьФормуПоМакету).
НастройкиМакета = ЛксСоздатьРеквизитНастройкиМакета(ЭтаФорма);
НастройкиМакета.Добавить(<ДобавленныйЭлемент>);
НастройкиМакета.Добавить(<ЭлементСИзмененнойПозицией>, "Позиция");
НастройкиМакета.Добавить(<ЭлементСИзмененнымиРазмеромИПривязкой>, "Размер_Привязка");
НастройкиМакета.Добавить(<КоманднаяПанельСДобавленнойКнопкой>, "Внутри");

В обработчике ПередОткрытием рабочей (типовой) формы нужно вызвать процедуру настройки формы по макету:
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)

    ЛксНастроитьФормуПоМакету(ЭтаФорма);
    [...]

КонецПроцедуры // ПередОткрытием()

Либо с применением моего метода Книга знаний: v8: Методика переопределения и вызова обработчиков событий формы без модификации типового обработчика:
// Процедура - обработчик события "ПередОткрытием" элемента формы "".
//
Процедура ЛксПередОткрытием(Отказ, СтандартнаяОбработка)

    ЛксНастроитьФормуПоМакету(ЭтаФорма);
    Выполнить(ЛксПолучитьСтароеДействиеФормы(ЭтаФорма, "ПередОткрытием", ""));    

КонецПроцедуры // ЛксПередОткрытием()

ЛксУстановитьДействиеФормы(ЭтаФорма, "ПередОткрытием", "");


Необходимые функции и процедуры общего модуля.


// Создает невидимый реквизит макета формы.
// В этот реквизит потом будет помещен список настроек макета.
// Вызывается в самом начале основной программы модуля макета формы.
//
// Параметры:
//  пФорма       – Форма – макета формы.
//
// Возвращаемое значение:
//               - СписокЗначений – список выбора созданного элемента формы.
//
Функция ЛксСоздатьРеквизитНастройкиМакета(пФорма) Экспорт

    Возврат пФорма.ЭлементыФормы.Добавить(Тип("ПолеВвода"), "общНастройкиМакетаФормы").СписокВыбора;

КонецФункции // ЛксПолучитьРеквизитНастройкиМакета()

// Заполняет форму по ее макету. Используется для динамического добавления элементов
// в типовые формы, чтобы облегчить их обновление. Макет формы, если явно не указан,
// ищется среди форм объекта метаданных формы по имени "Лкс"+<ИмяФормы>+"Макет".
// Список настраиваемых элементов формы берется из свойства СписокВыбора предварительно
// программно добавленного элемента формы общНастройкиМакетаФормы.
// Для измененных элементов формы при внесеннии в список добавляется
// представление в соответствии с изменениями, содержащее ключевые слова с разделителем "_":
// "Привязка", "Размер", "Позиция", "Внутри" (для коллекций).
// Следует вызывать в обработчике ПередОткрытием формы.
//   Ограничения.
// 1. Без явного указания макета работает только для основной формы объекта.
// 2. Нельзя добавлять элементы в панели и поля табличного документа, т.к. у элемента нельзя
// определить родителя.
// 3. Нельзя, чтобы форма и макет имели разные размеры. Обрабатывается.
// 4. Нельзя добавлять и изменять элементы, привязанные косвенно к низу формы.
// 5. Иногда элементы, привязанные косвенно к правой границе формы неверно располагаются.
//
// Параметры:
//  пФорма       – Форма – которую настраиваем;
//  *пМакет      – Форма - макет, по которому настраиваем.
//
Процедура ЛксНастроитьФормуПоМакетуСНастройками(пФорма, пМакетФормы = Неопределено) Экспорт

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


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

Для вложенных элементов управления можно оптимизировать описание модификаций, логически развив идею. Например, добавляя в список не сам элемент формы, а путь к нему:
НастройкиМакета.Добавить("ОсновныеДействияФормы.Печать.МояПечать");

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

Конечно, главная функция ЛксНастроитьФормуПоМакету еще многого не умеет, например добавить колонку в табличное поле. Но это можно быстро дописать. У меня пока не было такой потребности. Нельзя также не отметить большие ограничения на характер модификаций диалога, большинство из которых являются бесспорными недоработками реализации движка интерфейса пользователя. Печально, но эти ограничения сохранились и в 1с 8.1 бета.

От Гения1С: Молодец, ТормозИТ, но исторически эту фишку придумал я. :-) Книга знаний: v8: Копировальщик форм - вложенные формы
TormozIT: Признаю, ты первый придумал, но уклон опять же был в другую сторону. Я довел идею до приемлимого уровня применимости и документированности. Моя заслуга здесь неоспорима =)
От Гения1С: Можно ускориться, если сохранять готовую форму в хранилище и просто вызывать ее оттуда.

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

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