Как программно назначить обработчик события «ПриАктивизацииОбласти» для динамически созданного поля табличного документа в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке сложных и гибких интерфейсов в среде «1С:Предприятие» разработчики нередко прибегают к динамическому конструированию форм. Создание элементов управления прямо из кода позволяет адаптировать рабочее пространство под специфические задачи пользователя. Одна из частых задач в рамках этой практики — программное создание нескольких полей табличных документов (например, вкладок с отчетами или сравнительных таблиц) и отслеживание действий пользователя внутри них.

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

Анализ задачи и архитектурный подход

Представим сценарий: на форме динамически создается несколько полей табличного документа (например, Таб1, Таб2, Таб3). Нам необходимо сделать так, чтобы при выделении пользователем ячеек в любом из этих документов срабатывал единый алгоритм, который анализирует выделенную область (например, вычисляет сумму числовых значений, как в строке состояния Microsoft Excel). Для реализации этого поведения нам нужно решить две задачи:

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

Рассмотрим пошагово реализацию этой логики для различных типов форм платформы «1С:Предприятие».

Решение для Обычных форм (Обычное приложение)

В обычном приложении для связывания события программно созданного элемента формы с процедурой-обработчиком используется метод УстановитьДействие(). Этот метод принимает два параметра: имя события в виде строки и специальный объект типа Действие, в конструктор которого передается имя процедуры-обработчика, расположенной в модуле этой же формы.

Проанализируем пример кода, в котором мы динамически создаем поле табличного документа на панели формы и сразу же привязываем к нему обработчик события активации ячеек:


// Программное создание поля табличного документа в Обычной форме
НовоеПоле = ЭлементыФормы.Добавить(Тип("ПолеТабличногоДокумента"), "ТабДок_Динамический", Истина, ЭлементыФормы.ПанельОсновная);
НовоеПоле.Верх = 10;
НовоеПоле.Лево = 10;
НовоеПоле.Ширина = 400;
НовоеПоле.Высота = 300;

// Связываем событие "ПриАктивизацииОбласти" с процедурой-обработчиком
ИмяПроцедурыОбработчика = "ПриАктивизацииОбластиДинамическогоТабДока";
НовоеПоле.УстановитьДействие("ПриАктивизацииОбласти", Новый Действие(ИмяПроцедурыОбработчика));

Если на форме уже существует эталонное поле табличного документа, в котором обработчик настроен вручную на этапе разработки, мы можем скопировать действие из него в новый элемент с помощью метода ПолучитьДействие():


// Копирование действия из существующего элемента формы
ДействиеИзЭталона = ЭлементыФормы.ЭталонныйТабличныйДокумент.ПолучитьДействие("ПриАктивизацииОбласти");
НовоеПоле.УстановитьДействие("ПриАктивизацииОбласти", ДействиеИзЭталона);

Решение для Управляемых форм (Управляемое приложение)

При работе с управляемыми формами (актуально для платформы 8.3) объект Действие больше не используется. Попытка применить его вызовет критическую ошибку выполнения. В управляемом интерфейсе метод УстановитьДействие() вторым параметром принимает обычную **строку** с именем процедуры-обработчика.

Разберем пример создания элемента для управляемой формы:


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

Важное примечание для Управляемых форм: Сама процедура-обработчик обязательно должна быть объявлена в модуле формы с директивой компиляции &НаКлиенте.

Переименование события в современных версиях платформы 1С 8.3

Разработчикам современных конфигураций крайне важно учитывать системные изменения платформы. Начиная с версии платформы 8.3.14 (при условии, что у конфигурации отключен режим совместимости с версиями 8.3.13 и ниже), событие ПриАктивизацииОбласти для поля табличного документа было переименовано разработчиками платформы в ПриАктивизации.

Если вы разрабатываете решение для современной базы данных без режима совместимости, ваш код назначения события должен выглядеть следующим образом:


// Для современных конфигураций на платформе 8.3.14+
НовыйЭлемент.УстановитьДействие("ПриАктивизации", "ПриАктивизацииДинамическогоТабДока");

Опасность зацикливания событий (Рекурсия)

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

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


&НаКлиенте
Процедура ПриАктивизацииОбластиДинамическогоТабДока(Элемент)
    
    // 1. Временно отключаем обработчик во избежание рекурсивного вызова
    Элемент.УстановитьДействие("ПриАктивизацииОбласти", ""); // или Неопределено для Обычных форм
    
    Попытка
        // 2. Выполняем полезную работу с табличным документом
        ВыполнитьОбработкуВыделеннойОбласти(Элемент);
    Исключение
        // На случай непредвиденных ошибок гарантируем возврат обработчика
        ЗаписьЖурналаРегистрации("Ошибка обработки события", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
    
    // 3. Восстанавливаем обработчик события
    Элемент.УстановитьДействие("ПриАктивизацииОбласти", "ПриАктивизацииОбластиДинамическогоТабДока");
    
КонецПроцедуры

Реализация алгоритма подсчета суммы выделенных ячеек

Теперь рассмотрим практический пример реализации самого алгоритма подсчета суммы выделенных чисел. Наш алгоритм должен учитывать несколько важных нюансов реальной работы пользователей:

Рассмотрим по шагам готовый код обработчика, учитывающий все перечисленные требования:


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

Рассмотренный выше комплексный подход обеспечивает бесперебойную и быструю работу интерфейса 1С. Динамически созданные элементы формы будут вести себя прогнозируемо, исключая зависания программы и предоставляя пользователю удобный интерактивный функционал анализа данных «на лету».

← На главную