Рассмотрим весьма распространенную задачу при разработке в 1С: на управляемой форме есть Таблица Значений (ТЗ), и нам необходимо перехватить событие установки отбора (фильтрации) пользователем. В старых версиях платформы (толстый клиент) для этого успешно применялся метод ПодключитьОбработчикИзмененияДанных. Однако, при переходе на Управляемые формы (УФ), разработчики часто сталкиваются с тем, что стандартные средства перестают работать так, как ожидалось.
Проанализируем ситуацию подробнее. Главная причина, по которой мы хотим отследить отбор — это необходимость пересчета итогов в подвале таблицы только по отображаемым (отфильтрованным) строкам. Выясним причину сложностей: в управляемых формах 1С отсутствуют платформенные события, которые реагируют на интерактивные действия пользователя, связанные исключительно с оформлением, такие как стандартный поиск (Ctrl+F), отбор или сортировка Таблицы Значений. Данные в самой таблице при этом не меняются, меняется лишь их отображение.
Разберем по шагам, какие подходы не работают и почему, а затем рассмотрим эффективные архитектурные решения этой проблемы.
Прежде чем переходить к рабочим решениям, проанализируем попытки решить задачу "в лоб":
ПодключитьОбработчикОжидания, который будет проверять состояние таблицы каждую секунду. Мы настоятельно не рекомендуем этот подход, так как он создает постоянную и неоправданную нагрузку на клиентскую часть.Если нам все же необходимо пересчитать итоги, главная проблема — понять, какие именно строки сейчас отображаются на форме. Для этого в управляемых формах существует удобный метод элемента управления формой — ПроверитьСтроку().
Разберем алгоритм: мы в цикле обходим все строки исходной Таблицы Значений и проверяем их идентификаторы. Если метод возвращает Истина, значит строка удовлетворяет текущему отбору на форме и видима пользователю, следовательно, её нужно приплюсовать к нашим итогам.
Посмотрим на пример реализации данного подхода:
// Процедура пересчета итогов
&НаКлиенте
Процедура ПересчитатьИтогиСУчетомОтбора()
СуммаИтого = 0;
Для Каждого Стр Из ЭтаФорма.МояТаблицаЗначений Цикл
// Получаем идентификатор текущей строки ТЗ
ИдентификаторСтроки = Стр.ПолучитьИдентификатор();
// Проверяем, отображается ли строка в данный момент в элементе формы
Если Элементы.МояТаблица.ПроверитьСтроку(ИдентификаторСтроки) Тогда
СуммаИтого = СуммаИтого + Стр.СуммаДокумента;
КонецЕсли;
КонецЦикла;
// Присваиваем результат реквизиту подвала
ЭтаФорма.ИтогПоСумме = СуммаИтого;
КонецПроцедуры
Этот метод отлично работает для небольших таблиц. Вызывать эту процедуру можно после любых программных действий с таблицей или привязав к специфическим событиям пользовательского интерфейса (о которых поговорим ниже).
Поскольку штатных событий на интерактивное изменение свойства ОтборСтрок нет, лучшей практикой в современных конфигурациях считается блокировка стандартного механизма отбора и создание собственного пользовательского интерфейса для фильтрации.
Вынесем ключевые поля для фильтрации прямо на форму над нашей таблицей. Например, добавим поле ввода ОтборКонтрагент. К этому полю мы привяжем событие ПриИзменении. В этом событии мы программно сформируем структуру отбора и применим её к таблице.
Разберем по шагам создание своего отбора:
&НаКлиенте
Процедура ОтборКонтрагентПриИзменении(Элемент)
// 1. Формируем структуру отбора
СтруктураОтбора = Новый Структура;
Если ЗначениеЗаполнено(ЭтаФорма.ОтборКонтрагент) Тогда
СтруктураОтбора.Вставить("Контрагент", ЭтаФорма.ОтборКонтрагент);
КонецЕсли;
// 2. Устанавливаем отбор для элемента формы
// Свойство ОтборСтрок принимает только тип ФиксированнаяСтруктура
Элементы.МояТаблица.ОтборСтрок = Новый ФиксированнаяСтруктура(СтруктураОтбора);
// 3. Сразу вызываем нашу процедуру пересчета итогов
ПересчитатьИтогиСУчетомОтбора();
КонецПроцедуры
Этот паттерн проектирования позволяет полностью контролировать процесс фильтрации и гарантированно выполнять пересчет данных именно тогда, когда пользователь меняет параметры.
Проанализируем ситуацию глобально. Если в вашей Таблице Значений много строк, сложная логика, и пересчет с использованием метода ПроверитьСтроку() начинает "тормозить", это верный знак того, что архитектуру формы нужно менять. Лучшей практикой 1С является уход от Таблицы Значений в пользу Динамического списка.
Как это реализовать?
Если же вам все-таки требуется сложный программный расчет итогов, то именно для Динамического списка доступно то самое событие ПриОбновленииСоставаПользовательскихНастроекНаСервере. Оно срабатывает каждый раз, когда пользователь интерактивно меняет отбор, сортировку или группировку через стандартные настройки списка.
&НаСервере
Процедура МойСписокПриОбновленииСоставаПользовательскихНастроекНаСервере(Элемент)
// Здесь мы можем получить текущие отборы компоновщика настроек
// и выполнить свой сложный расчет на сервере
НастройкиКомпоновки = Элемент.КомпоновщикНастроек.ПолучитьНастройки();
// ... логика расчета ...
КонецПроцедуры
Подводя итоги, можно сказать, что прямого перехвата стандартного отбора ТЗ в управляемых формах нет по архитектурным причинам платформы. Однако, используя грамотное проектирование интерфейса (собственные поля отборов) или мощь платформенных механизмов (Динамические списки и СКД), мы можем элегантно и без "костылей" решить задачу пересчета итогов для пользователя.