v8: Методика переопределения обработчиков событий формы и элементов формы. Каждый из нас хотя бы раз задумывался над тем, как было бы удобно, если бы мы имели возможность переопределять обработчики событий формы и элементов формы. При этом сохранять возможность вызова старого обработчика, не привязываясь к его имени. | | Автор статьи: TormozIT | Редакторы: Гений 1С, Последняя редакция №12 от 04.07.06 | История URL: http://kb.mista.ru/article.php?id=268 | |
Ключевые слова: обработчик событие форма элемент обновление переопределение
Выражаю большу признательность одному из умнейших, Гению 1С, за использование его материалов.
Например, есть конфигурация, которая является доработанной типовой. Вы ее постоянно обновляете и добавляете все больше своих изменений. Обновлять в общем случае с каждым разом становится все труднее и дольше. Значительную часть ваший доработок составляют вставки в начало и конец обработчиков событий формы и элементов формы. А также новые обработчики, которые некоторые еще и назначают в режиме редактирования конфигурации (статически), что дополнительно затрудняет процесс обновления.
Предлагаемый метод состоит в том, чтобы объявить в модуле формы свои обработчики событий, которые будут вызывать старые обработчики и выполнять нужный код до и после этого вызова. Установка новых обработчиков будет производиться в разделе операторов основной программы формы путем вызова общей процедуры, скажем "УстановитьДействиеФормы". Эта процедура в динамически добавленном невидимом элементе формы будет сохранять все старые обработчики для каждого переопределенного события, что позволит довольно просто вызывать их через общую функцию, скажем "ПолучитьДействиеФормы". Следуя рекомендациям 1С, а также для удобства возьмем за правило: имена новых обработчиков следует формировать как <Префикс>[<ИмяЭлементаФормы>]<ИмяСобытия>.
Случай 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С.
Суть ее состоит в динамическом добавлении нулевых размеров служебного элемента формы для хранения в нем списка значений в свойстве СписокВыбора. Т.е. сначала проверяем есть ли элемент формы с заданным именем (в примере "общСтарыеОбработчики"). Если его нет, то создаем. Затем получаем обработчик нужного действия формы или элемента формы. Сохраняем его в СпискеВыбора служебного элемента формы. Устанавливаем новый обработчик, имя которого формируем по правилу "Лкс" + <пИмяЭлементаФормы> + <пИмяСобытия>.
// Переопределяет обработчик события формы или элемента формы.
// Сохраняет штатный обработчик события внутри формы и устанавливает новый.
//
// Параметры:
// пФорма – Форма – форма;
// пИмяСобытия – Строка – имя события;
// *пИмяЭлементаФормы – Строка – имя элемента формы.
//
Процедура УстановитьДействиеФормы(пФорма, пИмяСобытия, пИмяЭлементаФормы = "") Экспорт
Если пИмяЭлементаФормы <> "" Тогда
Объект = пФорма.ЭлементыФормы[пИмяЭлементаФормы];
Иначе
Объект = пФорма;
КонецЕсли;
ТекстМаркера = "общСтарыеОбработчики";
Если пФорма.ЭлементыФормы.Найти(ТекстМаркера) = Неопределено Тогда
ЭФ = пФорма.ЭлементыФормы.Добавить(Тип("ПолеВвода"), ТекстМаркера, Ложь);
КонецЕсли;
ПолноеИмяСобытия = пИмяЭлементаФормы + пИмяСобытия;
СтароеДействие = Объект.ПолучитьДействие(пИмяСобытия);
НовоеДействие = Новый Действие("Лкс" + ПолноеИмяСобытия);
Объект.УстановитьДействие(пИмяСобытия, НовоеДействие);
пФорма.ЭлементыФормы.общСтарыеОбработчики.СписокВыбора.Добавить(ПолноеИмяСобытия, СтароеДействие);
КонецПроцедуры // УстановитьДействиеФормы()
Теперь функция для получения выражения на языке 1С, которое будет выполняться в модуле каждого нашего обработчика с целью запуска старого обработчика и передачи ему нужных параметров. В случае, если старого обработчика не было назначено для события функция вернет пустую строку.
// Получаем текст для выполнения вызова штатного обработчика события формы или элемента формы.
//
// Параметры:
// пФорма – Форма – форма;
// пИмяСобытия – Строка – имя события;
// *пИмяЭлементаФормы – Строка – имя элемента формы.
//
// Возвращаемое значение:
// – Строка – текст для выполнения.
//
Функция ПолучитьДействиеФормы(пФорма, пИмяСобытия, пИмяЭлементаФормы = "") Экспорт
ПолноеИмяСобытия = пИмяЭлементаФормы + пИмяСобытия;
СтарыйОбработчик = "";
ЭлементСписка = пФорма.ЭлементыФормы.общСтарыеОбработчики.СписокВыбора.НайтиПоЗначению(ПолноеИмяСобытия);
Если ЭлементСписка <> Неопределено Тогда
СтарыйОбработчик = ЭлементСписка.Представление;
КонецЕсли;
Если СтарыйОбработчик <> "" Тогда
Аргументы = "";
Если пИмяЭлементаФормы <> "" Тогда
// Это событие элемента формы
Если пИмяСобытия = "ПроверкаПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Колонка)";
ИначеЕсли пИмяСобытия = "НачалоПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОкончаниеПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Перетаскивание" Тогда
Аргументы = "(Элемент, Параметры перетаскивания, СтандартнаяОбработка, Область)";
ИначеЕсли пИмяСобытия = "АвтоПодборТекста" Тогда
Аргументы = "(Элемент, Текст, ТекстАвтоПодбора, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Выбор" Тогда
Аргументы = "(Элемент, Область, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "НачалоВыбора" Тогда
Аргументы = "(Элемент, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОбработкаВыбора" Тогда
Аргументы = "(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОбработкаЗаписиНовогоОбъекта" Тогда
Аргументы = "(Элемент, Объект, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОбработкаРасшифровки" Тогда
Аргументы = "(Элемент, Расшифровка, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОкончаниеВводаТекста" Тогда
Аргументы = "(Элемент, Текст, Значение, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Открытие" Тогда
Аргументы = "(Элемент, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Очистка" Тогда
Аргументы = "(Элемент, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ПередНачаломДобавления" Тогда
Аргументы = "(Элемент, Отказ, Копирование)";
ИначеЕсли пИмяСобытия = "ПередНачаломИзменения" Тогда
Аргументы = "(Элемент, Отказ)";
ИначеЕсли пИмяСобытия = "ПередОкончаниемРедактирования" Тогда
Аргументы = "(Элемент, НоваяСтрока, ОтменаРедактирования, Отказ)";
ИначеЕсли пИмяСобытия = "ПередУдалением" Тогда
Аргументы = "(Элемент, Отказ)";
ИначеЕсли пИмяСобытия = "ПриВыводеСтроки" Тогда
Аргументы = "(Элемент, ОформлениеСтроки, ДанныеСтроки)";
ИначеЕсли пИмяСобытия = "ПриИзмененииСодержимогоОбласти" Тогда
Аргументы = "(Элемент, Область)";
ИначеЕсли пИмяСобытия = "ПриНачалеРедактирования" Тогда
Аргументы = "(Элемент, НоваяСтрока, Копирование)";
ИначеЕсли пИмяСобытия = "ПриОкончанииРедактирования" Тогда
Аргументы = "(Элемент, НоваяСтрока, Отмена редактирования)";
ИначеЕсли пИмяСобытия = "ПриОкончанииРедактированияИнтервала" Тогда
Аргументы = "(Элемент, Интервал, Отмена)";
ИначеЕсли пИмяСобытия = "ПриПолученииДанных" Тогда
Аргументы = "(Элемент, ОформленияСтрок)";
ИначеЕсли пИмяСобытия = "ПриСменеСтраницы" Тогда
Аргументы = "(Элемент, ТекущаяСтраница)";
ИначеЕсли пИмяСобытия = "ПроверкаПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Область)";
ИначеЕсли пИмяСобытия = "Регулирование" Тогда
Аргументы = "(Элемент, Направление, СтандартнаяОбработка)";
Иначе
Аргументы = "(Элемент)";
КонецЕсли;
Иначе
// Это событие формы
Попытка
Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(пФорма.Объект)) Тогда
// Это форма элемента справочника
Если пИмяСобытия = "ПередЗаписью" Тогда
Аргументы = "(Отказ)";
КонецЕсли;
ИначеЕсли Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(пФорма.Объект)) Тогда
// Это форма элемента документа
Если пИмяСобытия = "ПередЗаписью" Тогда
Аргументы = "(Отказ, РежимЗаписи, РежимПроведения)";
КонецЕсли;
КонецЕсли;
Исключение
КонецПопытки;
Если Аргументы = "" Тогда
Если пИмяСобытия = "ПриЗаписи" Тогда
Аргументы = "(Отказ)";
ИначеЕсли пИмяСобытия = "ПередОткрытием" Тогда
Аргументы = "(Отказ, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ПередЗакрытием" Тогда
Аргументы = "(Отказ, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОбработкаВыбора" Тогда
Аргументы = "(ЗначениеВыбора, Источник)";
ИначеЕсли пИмяСобытия = "ОбработкаАктивизацииОбъекта" Тогда
Аргументы = "(АктивныйОбъект, Источник)";
ИначеЕсли пИмяСобытия = "ОбработкаЗаписиНовогоОбъекта" Тогда
Аргументы = "(Объект, Источник)";
ИначеЕсли пИмяСобытия = "ОбработкаОповещения" Тогда
Аргументы = "(ИмяСобытия, Параметр, Источник)";
ИначеЕсли пИмяСобытия = "ПриПовторномОткрытии" Тогда
Аргументы = "(СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = " ВнешнееСобытие" Тогда
Аргументы = "(Источник, Событие, Данные)";
Иначе
Аргументы = "()";
КонецЕсли;
КонецЕсли;
КонецЕсли;
СтарыйОбработчик = СтарыйОбработчик + Аргументы;
КонецЕсли;
Возврат СтарыйОбработчик;
КонецФункции // ПолучитьДействиеФормы()
|