Работа с отборами в системе компоновки данных (СКД) часто вызывает вопросы у разработчиков, особенно когда дело касается управляемых форм. Распространенная ситуация: программист пишет код для удаления отбора, но визуально на форме ничего не меняется, или отчет продолжает формироваться с учетом старых данных. В этой статье мы подробно разберем, почему так происходит, и изучим правильные методы управления отборами.
Первое, что нам необходимо понять — это архитектура хранения настроек в КомпоновщикНастроек. В СКД существует два уровня настроек:
КомпоновщикНастроек.Настройки): это структура, заданная в схеме компоновки по умолчанию или программно измененная база.КомпоновщикНастроек.ПользовательскиеНастройки): это те значения, которые пользователь видит и редактирует непосредственно на форме отчета (флажки, поля ввода, условия).Важный нюанс: если элемент отбора включен в состав пользовательских настроек (у него установлен соответствующий флаг в конфигураторе или программно), то значения из ПользовательскиеНастройки имеют абсолютный приоритет над общими настройками. Именно поэтому попытки очистить КомпоновщикНастроек.Настройки.Отбор.Элементы часто не дают видимого результата — форма продолжает отображать данные из пользовательского набора. Для детального разбора механизмов связи элементов управляемой формы и настроек отчета можно использовать публикацию по стыковке элементов формы и пользовательских настроек СКД.
Если перед нами стоит задача полностью очистить все установленные фильтры, мы должны избегать удаления элементов в цикле Для Каждого. Рассмотрим типичную ошибку начинающих разработчиков:
// ОШИБОЧНЫЙ КОД
Для Каждого Стр Из КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
КомпоновщикНастроек.Настройки.Отбор.Элементы.Удалить(Стр);
КонецЦикла;
Проблема этого кода заключается в том, что при удалении первого элемента индексы всех последующих смещаются, и цикл «проскакивает» объекты. Чтобы сделать это правильно, проанализируем два корректных способа:
Очистить(), который разом удаляет все элементы коллекции.Однако, помня о приоритете пользовательских настроек, нам нужно воздействовать именно на них. Если мы хотим сбросить всё до состояния «по умолчанию», лучше всего использовать метод ЗагрузитьНастройки():
// Сброс настроек к заводским (из схемы СКД)
Схема = Отчет.СхемаКомпоновкиДанных;
КомпоновщикНастроек.ЗагрузитьНастройки(Схема.НастройкиПоУмолчанию);
Чаще всего нам нужно не удалить все отборы, а очистить конкретное поле (например, «Склад» или «Контрагент»). Для этого нам нужно найти элемент в Настройки, получить его ИдентификаторПользовательскойНастройки и по этому идентификатору найти соответствующий элемент в ПользовательскиеНастройки.
Рассмотрим алгоритм поиска по шагам:
// 1. Определяем поле, которое ищем
ПолеСклад = Новый ПолеКомпоновкиДанных("Склад");
// 2. Ищем элемент в основных настройках
ЭлементНастройки = Неопределено;
Для Каждого Элемент Из КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных")
И Элемент.ЛевоеЗначение = ПолеСклад Тогда
ЭлементНастройки = Элемент;
Прервать;
КонецЕсли;
КонецЦикла;
// 3. Если нашли, меняем связанную пользовательскую настройку
Если ЭлементНастройки <> Неопределено Тогда
ID = ЭлементНастройки.ИдентификаторПользовательскойНастройки;
ПользовательскийЭлемент = КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ID);
Если ПользовательскийЭлемент <> Неопределено Тогда
// Отключаем использование (снимаем галочку)
ПользовательскийЭлемент.Использование = Ложь;
// Или очищаем значение, если нужно оставить галочку
ПользовательскийЭлемент.ПравоеЗначение = Неопределено;
КонецЕсли;
КонецЕсли;
Проанализируем ситуацию, когда отбор находится не в корне, а внутри группы (например, в группе «ИЛИ»). Обычный цикл не увидит такие элементы. В этом случае нам потребуется рекурсивная процедура. Давайте создадим универсальный механизм для отключения отборов по имени поля.
Процедура ОтключитьОтборРекурсивно(ГруппаЭлементов, ИмяПоля)
ПолеПоиска = Новый ПолеКомпоновкиДанных(ИмяПоля);
Для Каждого Элемент Из ГруппаЭлементов.Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
// Заходим внутрь группы
ОтключитьОтборРекурсивно(Элемент, ИмяПоля);
ИначеЕсли ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
Если Элемент.ЛевоеЗначение = ПолеПоиска Тогда
// Работаем с пользовательской настройкой
ID = Элемент.ИдентификаторПользовательскойНастройки;
ПользовательскийЭлемент = КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ID);
Если ПользовательскийЭлемент <> Неопределено Тогда
ПользовательскийЭлемент.Использование = Ложь;
КонецЕсли;
// Также отключаем в основных настройках
Элемент.Использование = Ложь;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Вызов процедуры для корня отбора
ОтключитьОтборРекурсивно(КомпоновщикНастроек.Настройки.Отбор, "Организация");
Выясним еще одну важную причину, по которой код может «не срабатывать». Если вы меняете настройки программно (например, в обработчике нажатия кнопки), форма может не узнать о том, что данные внутри компоновщика изменились. Чтобы интерфейс обновился и пользователь увидел снятые флажки или пустые поля, в конце процедуры обязательно выполните команду обновления формы.
Для управляемых форм это делается следующим образом:
ЭтаФорма.ОбновитьОтображениеДанных();
Проанализируем, что лучше: удалять элемент или снимать флаг использования?
Использование = Ложь): Самый рекомендуемый вариант. Поле остается в списке быстрых отборов на форме, но перестает влиять на результат. Это удобно для пользователя, так как ему не нужно заново искать и добавлять поле через расширенные настройки.Удалить()): Полностью убирает элемент. Если поле было выведено на форму как «быстрый доступ», оно исчезнет. Используйте это только в том случае, если логика программы предполагает динамическое формирование состава доступных фильтров.Подведем итог: Чтобы программное управление отборами работало стабильно, всегда проверяйте наличие ИдентификаторПользовательскойНастройки. Именно через этот ID происходит связь между внутренними механизмами СКД и тем, что видит пользователь на своем экране. Работайте напрямую с коллекцией ПользовательскиеНастройки.Элементы, и тогда ваши изменения будут мгновенно отражаться в интерфейсе отчета.