Как отслеживать изменения интерактивного отбора и фильтровать строки по сложным условиям в ТаблицеЗначений на форме 1С?

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

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

  1. Как отследить момент, когда пользователь интерактивно изменяет отбор в ТаблицеЗначений (например, через контекстное меню "ПраваяКнопка - РасширенныйПоиск - Найти"), чтобы своевременно обновить итоговые значения?
  2. Как реализовать сложный отбор строк в ТаблицеЗначений (например, "Сумма > 0") без необходимости добавлять дополнительные вспомогательные колонки?

Давайте разберем эти вопросы подробно и предложим эффективные решения.

Отслеживание интерактивного отбора и пересчет итогов в ТаблицеЗначений

Когда мы используем ТаблицуЗначений как реквизит формы и отображаем ее в табличном поле, мы ожидаем, что при изменении пользовательского отбора можно будет динамически пересчитывать итоги. Например, если в подвале табличного поля отображается общая сумма, после применения отбора она должна измениться, чтобы отражать только видимые строки.

Отсутствие прямого события

К сожалению, в платформе 1С:Предприятие для стандартного элемента формы, связанного с ТаблицейЗначений, нет прямого события, которое срабатывает при интерактивной установке или изменении отбора пользователем через контекстное меню. Это значит, что такие методы, как "ПраваяКнопка - РасширенныйПоиск - Найти", не вызывают специфического события, которое мы могли бы обработать в коде модуля формы.

В отличие от ДинамическогоСписка, где есть метод ПодключитьОбработчикИзмененияДанных для отслеживания изменений отбора, ТаблицаЗначений как реквизит формы не предоставляет аналогичной функциональности. Это создает определенные трудности, но не делает задачу неразрешимой.

Методы обхода: Как реагировать на изменение отбора?

Поскольку прямого события нет, мы вынуждены прибегать к обходным путям. Рассмотрим несколько подходов, которые помогут нам отреагировать на изменение интерактивного отбора и пересчитать итоги.

Использование таймера для периодической проверки

Один из наиболее универсальных, хотя и не всегда оптимальных по производительности, способов – это периодическая проверка состояния отбора с помощью таймера. Мы можем создать фоновое задание или использовать клиентский таймер для регулярной проверки того, изменился ли отбор табличного поля.

  1. Запуск таймера: При открытии формы или создании элемента формы, связанного с таблицей, мы запускаем таймер.
  2. Периодическая проверка: В обработчике события таймера мы сравниваем текущий отбор Элементы.Таблица1.ОтборСтрок с состоянием, сохраненным при предыдущей проверке. Если отбор изменился, или если изменилось количество видимых строк, мы пересчитываем итоги.
  3. Остановка таймера: Таймер следует останавливать при закрытии формы или уничтожении элемента.

Рассмотрим пример реализации с использованием клиентского таймера:


&НаКлиенте
Перем м_ИдентификаторТаймера;
Перем м_ПредыдущийОтборСтрок;
Перем м_ПредыдущееКоличествоСтрок;

Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Инициализация Таблицы1
    Таблица1.Колонки.Добавить("Сумма");
    Таблица1.Колонки.Добавить("Наименование");

    Для Инд = 1 По 10 Цикл
        НоваяСтрока = Таблица1.Добавить();
        НоваяСтрока.Сумма = Инд * 10;
        НоваяСтрока.Наименование = "Элемент " + Инд;
    КонецЦикла;

    // Вычисляем первоначальные итоги
    ОбновитьИтогиТаблицы1();
КонецПроцедуры

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    // Запускаем таймер с интервалом в 1 секунду
    м_ИдентификаторТаймера = ПодключитьОбработчикОжидания("ПроверитьОтборИтоги", 1, Истина);
    
    // Сохраняем начальное состояние
    м_ПредыдущийОтборСтрок = ПолучитьСтрокуПредставленияОтбора(Элементы.Таблица1.ОтборСтрок);
    м_ПредыдущееКоличествоСтрок = Элементы.Таблица1.КоличествоВидимыхСтрок;
КонецПроцедуры

&НаКлиенте
Процедура ПриЗакрытии()
    // Отключаем таймер при закрытии формы
    Если м_ИдентификаторТаймера <> Неопределено Тогда
        ОтключитьОбработчикОжидания(м_ИдентификаторТаймера);
        м_ИдентификаторТаймера = Неопределено;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Функция ПолучитьСтрокуПредставленияОтбора(ОтборСтруктура)
    Если ОтборСтруктура = Неопределено Тогда
        Возврат "";
    КонецЕсли;

    МассивСтрок = Новый Массив;
    Для Каждое ЭлементСтруктуры Из ОтборСтруктура Цикл
        МассивСтрок.Добавить(ЭлементСтруктуры.Ключ + "=" + Строка(ЭлементСтруктуры.Значение));
    КонецЦикла;
    Возврат СтрСоединить(МассивСтрок, ";");
КонецФункции

&НаКлиенте
Процедура ПроверитьОтборИтоги()
    ТекущийОтборСтрок = ПолучитьСтрокуПредставленияОтбора(Элементы.Таблица1.ОтборСтрок);
    ТекущееКоличествоСтрок = Элементы.Таблица1.КоличествоВидимыхСтрок;

    Если ТекущийОтборСтрок <> м_ПредыдущийОтборСтрок ИЛИ ТекущееКоличествоСтрок <> м_ПредыдущееКоличествоСтрок Тогда
        ОбновитьИтогиТаблицы1();
        м_ПредыдущийОтборСтрок = ТекущийОтборСтрок;
        м_ПредыдущееКоличествоСтрок = ТекущееКоличествоСтрок;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьИтогиТаблицы1()
    СуммаИтог = 0;
    // Итерируем по видимым строкам.
    // Если ОтборСтрок не установлен, это будут все строки.
    // Если интерактивный отбор установлен, КоличествоВидимыхСтрок
    // и строки, доступные через Элементы.Таблица1.ПолучитьЭлементы(),
    // будут соответствовать отфильтрованному состоянию.
    Для Каждого СтрокаТЗ Из Элементы.Таблица1.ПолучитьЭлементы() Цикл
        СуммаИтог = СуммаИтог + СтрокаТЗ.ДанныеСтроки.Сумма;
    КонецЦикла;
    Элементы.Таблица1Сумма.ТекстПодвала = Строка(СуммаИтог);
КонецПроцедуры

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

Применение события ПриАктивизацииСтроки

Как было верно замечено в обсуждении, событие ПриАктивизацииСтроки для табличного поля срабатывает не только при смене активной строки, но и при поиске. Это может быть полезным косвенным индикатором того, что пользователь взаимодействовал с механизмом поиска/отбора.

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

Пример использования ПриАктивизацииСтроки:


&НаКлиенте
Процедура Таблица1ПриАктивизацииСтроки(Элемент)
    // В этом обработчике можно обновить итоги,
    // так как активация строки часто следует за применением отбора/поиска.
    ОбновитьИтогиТаблицы1();
КонецПроцедуры

// Функция ОбновитьИтогиТаблицы1() остается такой же, как в примере с таймером
// (см. выше)

Преимущества: Простота реализации, более "естественное" реагирование на действия пользователя, чем таймер. Недостатки: Не гарантирует срабатывание при *любом* изменении отбора (например, если отбор применился, но активная строка не изменилась). Может срабатывать слишком часто, если пользователь активно перемещается по строкам, что может привести к излишним пересчетам.

Создание собственных элементов управления для отбора

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

  1. Поля ввода: Добавляем на форму поля ввода для критериев отбора (например, "Сумма больше", "Наименование содержит").
  2. Кнопка "Применить отбор": Рядом с полями ввода размещаем кнопку.
  3. Обработчик кнопки: В обработчике кнопки мы программно формируем отбор для ТаблицыЗначений и после его применения пересчитываем итоги.

Этот метод дает нам полный контроль над процессом: мы точно знаем, когда отбор был изменен, и можем гарантировать своевременный пересчет итогов.

Пример реализации:


// В реквизитах формы:
// СуммаОтбора (Число)
// НаименованиеОтбора (Строка)

&НаКлиенте
Процедура ПрименитьОтбор(Команда)
    СтруктураОтбора = Новый Структура;
    Если СуммаОтбора <> 0 Тогда
        // Здесь мы столкнемся с ограничением ОтборСтрок для "больше".
        // Для такого сценария нужно использовать программный отбор, как описано ниже.
        // Покажем пример для точного равенства, если бы оно было нужно.
        // СтруктураОтбора.Вставить("Сумма", СуммаОтбора);
    КонецЕсли;

    Если Не ПустаяСтрока(НаименованиеОтбора) Тогда
        СтруктураОтбора.Вставить("Наименование", НаименованиеОтбора);
    КонецЕсли;

    // Для ТаблицыЗначений свойство ОтборСтрок поддерживает только равенство
    // или работу с набором значений. Для "больше нуля" потребуется
    // вызывать отдельную функцию для программной фильтрации.
    Если СтруктураОтбора.Количество() > 0 Тогда
        // Пример для ОтборСтрок (поддерживает только равенство)
        // Элементы.Таблица1.ОтборСтрок = Новый ФиксированнаяСтруктура(СтруктураОтбора);

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

    ОбновитьИтогиТаблицы1();
КонецПроцедуры

// (Функции ОтфильтроватьТаблицу1Программно и ОтменитьОтборТаблицы1Программно
// будут описаны в следующем разделе)

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

Реализация отбора строк в ТаблицеЗначений по условию "больше нуля" или "не равно"

Второй частый вопрос касается возможности фильтрации строк в ТаблицеЗначений по сложным условиям, таким как "значение > 0" или "значение НЕ равно X", при этом желательно избежать добавления вспомогательных колонок в таблицу для индикации отбора.

Ограничения свойства ОтборСтрок

Свойство ОтборСтрок расширения табличного поля формы для ТаблицыЗначений (как и для ТабличнойЧасти) действительно позволяет устанавливать отбор по значениям колонок с помощью ФиксированнойСтруктуры. Однако оно в основном поддерживает только сравнение на равенство или нахождение значения в заданном списке. Прямые операторы сравнения типа "больше", "меньше", "не равно" или логические "И", "ИЛИ" через ОтборСтрок напрямую не поддерживаются для сложных выражений.

Например, следующий код отберет строки, где Сумма равна 0:

Элементы.Таблица1.ОтборСтрок = Новый ФиксированнаяСтруктура("Сумма", 0);

Но как быть, если нам нужно получить строки, где Сумма > 0 или Сумма <> 0? Здесь нам понадобятся более мощные инструменты.

Гибкие методы программной фильтрации ТаблицыЗначений

Для реализации более сложных условий отбора, таких как "значение > 0", "НЕ" или комбинации условий, мы можем использовать следующие подходы.

Отбор с использованием запроса (ПостроительЗапроса)

Одним из наиболее гибких и мощных способов является использование языка запросов 1С. ТаблицуЗначений можно выгрузить во временную таблицу запроса, а затем построить запрос с любыми необходимыми условиями отбора. После выполнения запроса результат выгружается обратно в ТаблицуЗначений на форме.

  1. Создание ПостроителяЗапроса: Инициализируем объект ПостроительЗапроса.
  2. Выгрузка ТаблицыЗначений во временную таблицу: Используем метод УстановитьПараметр для передачи нашей ТаблицыЗначений в запрос как временную таблицу.
  3. Формирование запроса: Пишем запрос, который выбирает данные из временной таблицы с нужными условиями в секции ГДЕ.
  4. Выполнение запроса и загрузка результата: Выполняем запрос и загружаем полученную выборку обратно в ТаблицуЗначений, которая привязана к табличному полю формы.

Этот подход позволяет использовать всю мощь языка запросов 1С, включая сложные выражения, функции и операторы сравнения.

Рассмотрим пример отбора строк, где колонка "Сумма" больше заданного значения:


// В реквизитах формы:
// Таблица1 (ТаблицаЗначений) - наша исходная таблица
// Таблица1Полная (ТаблицаЗначений) - копия исходной таблицы для сброса отбора

&НаСервере
Процедура ОтфильтроватьТаблицу1СПомощьюЗапроса(ЗначениеСуммыДляОтбора)

    Если Таблица1Полная = Неопределено ИЛИ Таблица1Полная.Количество() = 0 Тогда
        // Сохраним полную копию таблицы, чтобы можно было сбросить отбор
        Таблица1Полная = Таблица1.Скопировать();
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЗ.Сумма,
    |    ТЗ.Наименование
    |ИЗ
    |    &ВходящаяТаблица КАК ТЗ
    |ГДЕ
    |    ТЗ.Сумма > &СуммаОтбора";
    
    Запрос.УстановитьПараметр("ВходящаяТаблица", Таблица1Полная);
    Запрос.УстановитьПараметр("СуммаОтбора", ЗначениеСуммыДляОтбора);
    
    Выборка = Запрос.Выполнить().Выгрузить();
    
    Таблица1.Очистить();
    Для Каждого СтрокаВыборки Из Выборка Цикл
        НоваяСтрока = Таблица1.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаВыборки);
    КонецЦикла;

    // Или, если Таблица1 является реквизитом формы и Выгрузить() возвращает
    // ТаблицуЗначений, можно так:
    // Таблица1 = Запрос.Выполнить().Выгрузить();

КонецПроцедуры

&НаСервере
Процедура ОтменитьОтборТаблицы1Программно()
    Если Таблица1Полная <> Неопределено Тогда
        Таблица1.Очистить();
        Для Каждого СтрокаПолнойТЗ Из Таблица1Полная Цикл
            НоваяСтрока = Таблица1.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаПолнойТЗ);
        КонецЦикла;
        // Или: Таблица1 = Таблица1Полная.Скопировать();
    КонецЕсли;
КонецПроцедуры

// Пример вызова с клиента:
&НаКлиенте
Процедура ПрименитьОтборПоСуммеБольшейНуля(Команда)
    ОтфильтроватьТаблицу1СПомощьюЗапроса(0);
    ОбновитьИтогиТаблицы1();
КонецПроцедуры

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

Программное копирование и фильтрация строк

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

  1. Создание новой ТаблицыЗначений: Создаем пустую ТаблицуЗначений с такой же структурой, как исходная.
  2. Перебор и фильтрация: В цикле перебираем каждую строку исходной таблицы. Если строка удовлетворяет условию (например, Строка.Сумма > 0), добавляем ее в новую таблицу.
  3. Присвоение: Присваиваем новую, отфильтрованную ТаблицуЗначений реквизиту формы, к которому привязано табличное поле.

Этот метод интуитивно понятен и легко реализуем для простых условий.

Пример программной фильтрации:


// В реквизитах формы:
// Таблица1 (ТаблицаЗначений) - наша исходная таблица
// Таблица1Полная (ТаблицаЗначений) - копия исходной таблицы для сброса отбора

&НаСервере
Процедура ОтфильтроватьТаблицу1Программно(ЗначениеСуммыДляОтбора = 0)

    Если Таблица1Полная = Неопределено ИЛИ Таблица1Полная.Количество() = 0 Тогда
        // Сохраним полную копию таблицы, чтобы можно было сбросить отбор
        Таблица1Полная = Таблица1.Скопировать();
    КонецЕсли;

    // Создаем новую таблицу для отфильтрованных данных
    ОтфильтрованнаяТЗ = Новый ТаблицаЗначений;
    Для Каждого Колонка Из Таблица1Полная.Колонки Цикл
        ОтфильтрованнаяТЗ.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения, Колонка.Заголовок, Колонка.Ширина);
    КонецЦикла;

    // Перебираем строки полной таблицы и добавляем подходящие в отфильтрованную
    Для Каждого СтрокаТЗ Из Таблица1Полная Цикл
        Если СтрокаТЗ.Сумма > ЗначениеСуммыДляОтбора Тогда
            НоваяСтрока = ОтфильтрованнаяТЗ.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗ);
        КонецЕсли;
    КонецЦикла;

    // Присваиваем отфильтрованную таблицу реквизиту формы
    Таблица1 = ОтфильтрованнаяТЗ;

КонецПроцедуры

&НаСервере
Процедура ОтменитьОтборТаблицы1Программно()
    Если Таблица1Полная <> Неопределено Тогда
        // Восстанавливаем исходную полную таблицу
        Таблица1 = Таблица1Полная.Скопировать();
    КонецЕсли;
КонецПроцедуры

// Пример вызова с клиента:
&НаКлиенте
Процедура ПрименитьОтборПоСуммеБольшейНуляПрограммно(Команда)
    ОтфильтроватьТаблицу1Программно(0);
    ОбновитьИтогиТаблицы1();
КонецПроцедуры

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

Использование метода НайтиСтроки() и его особенности

Метод НайтиСтроки() ТаблицыЗначений позволяет найти строки по соответствию значений в Структуре. Он возвращает массив найденных строк. Из этого массива можно дополнительно отфильтровать нужные строки или использовать его для выделения. Однако, как и ОтборСтрок табличного поля, НайтиСтроки() в основном используется для точного соответствия значений.

Пример использования НайтиСтроки() для поиска строк со значением "Сумма = 100":


&НаСервере
Процедура ПримерИспользованияНайтиСтроки()
    СтруктураПоиска = Новый Структура("Сумма", 100);
    НайденныеСтроки = Таблица1.НайтиСтроки(СтруктураПоиска); // НайденныеСтроки - это массив

    // Дальнейшая обработка найденных строк, например, для вычисления итога:
    СуммаНайденных = 0;
    Для Каждого СтрокаТЗ Из НайденныеСтроки Цикл
        СуммаНайденных = СуммаНайденных + СтрокаТЗ.Сумма;
    КонецЦикла;
    Сообщить("Сумма найденных строк: " + СуммаНайденных);
КонецПроцедуры

Для условий типа "больше нуля" НайтиСтроки() напрямую не подходит. Если нужна сложная логика, скорее всего, придется использовать Скопировать() с последующим перебором, как в предыдущем пункте, или запрос.

Визуальное выделение с помощью Условного оформления

Хотя это не является прямым отбором строк (то есть строки не скрываются), для визуального выделения строк, удовлетворяющих определенному условию (например, "Сумма > 0"), мы можем использовать условное оформление таблицы на форме. Этот механизм позволяет изменить цвет фона, текста, шрифт и другие параметры отображения строк или отдельных ячеек, делая их заметными для пользователя.

Настроить условное оформление можно в свойствах табличного поля, на вкладке "Условное оформление". Мы добавляем новый элемент оформления, указываем условие (например, Поле("Сумма") > 0) и задаем нужный вид оформления. Это может быть полезной альтернативой, если требование "скрыть строки" не является критичным, а достаточно лишь их выделить.

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

← На главную