Книга знаний

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

v8: Методика переопределения обработчиков событий формы и элементов формы.

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

Ключевые слова: обработчик событие форма элемент формы обновление переопределение перегрузка


Выражаю большу признательность одному из умнейших, Гению 1С, за использование его материалов.

 Например, есть конфигурация, которая является доработанной типовой. Вы ее постоянно обновляете и добавляете все больше своих изменений. Обновлять в общем случае с каждым разом становится все труднее и дольше. Значительную часть ваший доработок составляют вставки в начало и конец обработчиков событий формы и элементов формы. А также новые обработчики, которые некоторые еще и назначают в режиме редактирования конфигурации (статически), что дополнительно затрудняет процесс обновления.
 
 Предлагаемый метод состоит в том, чтобы объявить в модуле формы свои обработчики событий, которые будут вызывать старые обработчики и выполнять нужный код до и после этого вызова. Установка новых обработчиков будет производиться в разделе операторов основной программы формы путем вызова общей процедуры, скажем "УстановитьДействиеФормы". Эта процедура в динамически добавленном невидимом элементе формы будет сохранять все старые обработчики для каждого переопределенного события, что позволит довольно просто вызывать их через общую функцию, скажем "ПолучитьДействиеФормы". Следуя рекомендациям 1С, а также для удобства возьмем за правило:<b>имена новых обработчиков следует формировать как <Префикс>[<ИмяЭлементаФормы>]<ИмяСобытия></b>.

Случай 1. Добавляем вставку в начало и конец имеющегося в типовой обработчика события формы и элемента формы.


Обычно делают примерно так.
// Процедура - обработчик события "ПередОткрытием" формы.
//
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)

    // {{Добавил TormozIT 23.03.2006
    <Текст вставки в начало обработчика>
    //  }}Добавил TormozIT 23.03.2006

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

    // {{Добавил TormozIT 23.03.2006
    <Текст вставки в конец обработчика>
    //  }}Добавил TormozIT 23.03.2006
 
КонецПроцедуры // ПередОткрытием()

// Процедура - обработчик события "НачалоВыбора" поля ввода Склад
//
Процедура СкладНачалоВыбора(Элемент, СтандартнаяОбработка)

    // {{Добавил TormozIT 23.03.2006
    <Текст вставки в начало обработчика>
    //  }}Добавил TormozIT 23.03.2006
    НачалоВыбораСкладаНТТ(ЭтотОбъект, ЭтаФорма, Элемент, Склад, ВидСравнения.НеРавно, СтандартнаяОбработка);
    // {{Добавил TormozIT 23.03.2006
    <Текст вставки в конец обработчика>
    //  }}Добавил TormozIT 23.03.2006

КонецПроцедуры // СкладНачалоВыбора()

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

Теперь следует делать так.
// {{Добавил TormozIT 23.03.2006

// Процедура - обработчик события "ПередОткрытием" формы 
// 
Процедура ЛксПередОткрытием(Отказ, СтандартнаяОбработка)
    
    <Текст вставки в начало обработчика>
    Выполнить(ПолучитьДействиеФормы(ЭтаФорма, "ПередОткрытием"));
    <Текст вставки в конец обработчика>
    
КонецПроцедуры // ЛксПередОткрытием() 

// Процедура - обработчик события "НачалоВыбора" элемента формы "Склад"
//
Процедура ЛксСкладНачалоВыбора(Элемент, СтандартнаяОбработка)
    
    <Текст вставки в начало обработчика>
    Выполнить(ПолучитьДействиеФормы(ЭтаФорма, "НачалоВыбора", "Склад"));
    <Текст вставки в конец обработчика>
    
КонецПроцедуры // ЛксСкладНачалоВыбора()

//  }}Добавил TormozIT 23.03.2006 
//////////////////////////////////////////////////////////////////////////////// 
// ОПЕРАТОРЫ ОСНОВНОЙ ПРОГРАММЫ
 
// {{Добавил TormozIT 23.03.2006
УстановитьДействиеФормы(ЭтаФорма, "ПередОткрытием");
УстановитьДействиеФормы(ЭтаФорма, "НачалоВыбора"  , "Склад");
//  }}Добавил TormozIT 23.03.2006 

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

Случай 2. Добавляем новый обработчик события формы и элемента формы (включая новые элементы формы!).


Обычно делают примерно так.
// {{Добавил TormozIT 23.03.2006

// Процедура - обработчик события "ПриИзменении" элемента формы "Склад"
//
Процедура СкладПриИзменении(Элемент, СтандартнаяОбработка)
    
    <Текст начала обработчика>
    <Текст конца обработчика>
    
КонецПроцедуры // СкладПриИзменении()

//  }}Добавил TormozIT 23.03.2006

Все работает и выглядит хорошо.
Но подумайте, что будет, если в новом релизе будет добавлен в режиме редактирования конфигурации (статически) связанный обработчик для того же события и, возможно, что еще хуже, с таким же именем!
Будет неприятно.

Как следует поступать в этом случае?
Опять по той же схеме.
// {{Добавил TormozIT 23.03.2006

// Процедура - обработчик события "ПриИзменении" элемента формы "Склад"
//
Процедура ЛксСкладПриИзменении(Элемент, СтандартнаяОбработка)
    
    <Текст начала обработчика>
    Выполнить(ПолучитьДействиеФормы(ЭтаФорма, "ПриИзменении", "Склад"));
    <Текст конца обработчика>
    
КонецПроцедуры // ЛксСкладПриИзменении()

//  }}Добавил TormozIT 23.03.2006
...
//////////////////////////////////////////////////////////////////////////////// 
// ОПЕРАТОРЫ ОСНОВНОЙ ПРОГРАММЫ

// {{Добавил TormozIT 23.03.2006
УстановитьДействиеФормы(ЭтаФорма, "ПриИзменении"  , "Склад");
//  }}Добавил TormozIT 23.03.2006

Теперь уже нам не страшны добавленные в типовую обработчики событий и их имена!

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


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

    // Явное "определение" типа переменных.
    Если Ложь Тогда
        пФорма = ПолучитьОбщуюФорму(0);
        пЭлементФормы = пФорма.ЭлементыФормы[0];
    КонецЕсли;
    
    Если пИмяЭлементаФормы <> "" Тогда 
        Объект = пФорма.ЭлементыФормы[пИмяЭлементаФормы];
    Иначе
        Объект = пФорма;
    КонецЕсли;
    
    ТекстМаркера = "общСтарыеОбработчики";
    Если пФорма.ЭлементыФормы.Найти(ТекстМаркера) = Неопределено Тогда
        ЭФ = пФорма.ЭлементыФормы.Добавить(Тип("ПолеВвода"), ТекстМаркера, Ложь);
    КонецЕсли;
    
    ПолноеИмяСобытия = пИмяЭлементаФормы + пИмяСобытия;
    СтароеДействие = Объект.ПолучитьДействие(пИмяСобытия);
    НовоеДействие = Новый Действие("Лкс" + ПолноеИмяСобытия);
    Объект.УстановитьДействие(пИмяСобытия, НовоеДействие);
    пФорма.ЭлементыФормы.общСтарыеОбработчики.СписокВыбора.Добавить(ПолноеИмяСобытия, СтароеДействие);

КонецПроцедуры // УстановитьДействиеФормы()

Теперь функция для получения выражения на языке 1С, которое будет выполняться в модуле каждого нашего обработчика с целью запуска старого обработчика и передачи ему нужных параметров. В случае, если старого обработчика не было назначено для события функция вернет пустую строку.
// Получаем текст для выполнения вызова штатного обработчика события формы или элемента формы.
//
// Параметры:
//  пФорма       – Форма – форма;
//  пИмяСобытия  – Строка – имя события;
//  *пИмяЭлементаФормы – Строка – имя элемента формы.
//
// Возвращаемое значение:
//               – Строка – текст для выполнения.
//
Функция ПолучитьДействиеФормы(пФорма, пИмяСобытия, пИмяЭлементаФормы = "") Экспорт

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

КонецФункции // ПолучитьДействиеФормы()

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

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