Как правильно настроить флажки в таблицах на обычных формах 1С и избежать пересечения данных?

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

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

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

В чем заключается основная проблема исходного кода?

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

  1. Отрисовать флажки выбора в колонках Отметка для первой таблицы и Отметка2 для второй таблицы.
  2. Объединить логику вывода строк в одну процедуру, чтобы «отметки не пересекались».

При этом в коде автора присутствует целая серия критических ошибок:

Давайте шаг за шагом исправим эту архитектуру и напишем чистый, производительный код.

Способ 1. Автоматическая привязка флажков без использования событий формы (Рекомендуемый)

Платформа 1С:Предприятие умеет самостоятельно отображать и обрабатывать флажки в колонках табличного поля, если эти колонки правильно настроены. Нам не потребуется использовать события ПриВыводеСтроки и ПриИзмененииФлажка.

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

  1. Колонка в исходной таблице значений должна иметь тип Булево.
  2. У колонки табличного поля на форме свойство Данные должно быть пустым, чтобы система не пыталась выводить текст «Да» или «Нет».
  3. У колонки табличного поля на форме свойство ДанныеФлажка должно содержать имя колонки из таблицы значений (например, "Отметка").
  4. Свойство РежимРедактирования для этой колонки должно быть установлено в значение Непосредственно, чтобы пользователь мог менять значение флажка в один клик.

Рассмотрим, как правильно инициализировать данные при открытии формы в процедуре ПриОткрытии:


Процедура ПриОткрытии()
    
    // --- НАСТРОЙКА ТАБЛИЦЫ 1 (ТП1) ---
    Запрос1 = Новый Запрос;
    Запрос1.Текст = 
    "ВЫБРАТЬ
    |	ЛОЖЬ КАК Отметка,
    |	РезервРЦ.Номенклатура КАК Номенклатура,
    |	РезервРЦ.Характеристика КАК Характеристика,
    |	РезервРЦ.ЗарезервированоЗа КАК ЗарезервированоЗа,
    |	РезервРЦ.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    |	РезервРЦ.ПоДокументу КАК ПоДокументу,
    |	РезервРЦ.Количество КАК Количество
    |ИЗ
    |	РегистрСведений.РезервРЦ КАК РезервРЦ";
    
    ТП1 = Запрос1.Выполнить().Выгрузить();
    
    // Выполняем привязку данных к элементу формы
    ЭлементыФормы.ТП1.Значение = ТП1;
    ЭлементыФормы.ТП1.СоздатьКолонки();
    
    // Настраиваем колонку "Отметка" как интерактивный флажок
    КолонкаТП1 = ЭлементыФормы.ТП1.Колонки.Отметка;
    КолонкаТП1.Данные = ""; // Очищаем текстовое представление
    КолонкаТП1.ДанныеФлажка = "Отметка"; // Привязываем к булевой колонке ТЗ
    КолонкаТП1.РежимРедактирования = РежимРедактированияКолонки.Непосредственно;
    КолонкаТП1.Ширина = 6;
    КолонкаТП1.Заголовок = "Выб.";

    // --- НАСТРОЙКА ТАБЛИЦЫ 2 (ТП2) ---
    Запрос2 = Новый Запрос;
    Запрос2.Текст = 
    "ВЫБРАТЬ
    |	ЛОЖЬ КАК Отметка2,
    |	РезервРЦ.ПоДокументу КАК ПоДокументу,
    |	СУММА(РезервРЦ.Количество) КАК Количество
    |ИЗ
    |	РегистрСведений.РезервРЦ КАК РезервРЦ
    |
    |СГРУППИРОВАТЬ ПО
    |	РезервРЦ.ПоДокументу";
    
    ТП2 = Запрос2.Выполнить().Выгрузить();
    
    ЭлементыФормы.ТП2.Значение = ТП2;
    ЭлементыФормы.ТП2.СоздатьКолонки();
    
    // Настраиваем колонку "Отметка2"
    КолонкаТП2 = ЭлементыФормы.ТП2.Колонки.Отметка2;
    КолонкаТП2.Данные = "";
    КолонкаТП2.ДанныеФлажка = "Отметка2";
    КолонкаТП2.РежимРедактирования = РежимРедактированияКолонки.Непосредственно;
    КолонкаТП2.Ширина = 6;
    КолонкаТП2.Заголовок = "Выб.";

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

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

Способ 2. Объединение обработки вывода строк в одну процедуру

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

Для реализации этого способа укажите в свойствах обоих табличных полей (ТП1 и ТП2) в строке события «ПриВыводеСтроки» имя одной и той же процедуры, например, ОбщаяПриВыводеСтроки.

Разберем структуру этой процедуры:


Процедура ОбщаяПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
    
    // Проверяем, для какого именно табличного поля сработало событие
    Если Элемент = ЭлементыФормы.ТП1 Тогда
        
        // Оформление для первой таблицы
        Если ДанныеСтроки <> Неопределено Тогда
            ЯчейкаОтметки = ОформлениеСтроки.Ячейки.Отметка;
            ЯчейкаОтметки.ОтображатьФлажок = Истина;
            ЯчейкаОтметки.ОтображатьТекст  = Ложь;
            ЯчейкаОтметки.Флажок = ДанныеСтроки.Отметка;
            
            // Пример дополнительного оформления: выделение цветом выбранной строки
            Если ДанныеСтроки.Отметка Тогда
                ОформлениеСтроки.ЦветФона = Новый Цвет(230, 255, 230); // Светло-зеленый
            Иначе
                ОформлениеСтроки.ЦветФона = ЦветаСтиля.ЦветФонаКнопки;
            КонецЕсли;
        КонецЕсли;
        
    ИначеЕсли Элемент = ЭлементыФормы.ТП2 Тогда
        
        // Оформление для второй таблицы
        Если ДанныеСтроки <> Неопределено Тогда
            ЯчейкаОтметки = ОформлениеСтроки.Ячейки.Отметка2;
            ЯчейкаОтметки.ОтображатьФлажок = Истина;
            ЯчейкаОтметки.ОтображатьТекст  = Ложь;
            ЯчейкаОтметки.Флажок = ДанныеСтроки.Отметка2;
            
            Если ДанныеСтроки.Отметка2 Тогда
                ОформлениеСтроки.ЦветФона = Новый Цвет(230, 255, 230);
            Иначе
                ОформлениеСтроки.ЦветФона = ЦветаСтиля.ЦветФонаКнопки;
            КонецЕсли;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Благодаря явной проверке Если Элемент = ЭлементыФормы.ТП1, логика отрисовки разных таблиц никогда не пересечется, даже если они используют один общий программный обработчик.

Оптимизация работы с регистром сведений при удалении записей

В исходном коде автора при нажатии кнопки «Удалить» происходил обход строк таблицы с проверкой флага. Для каждой помеченной строки создавался набор записей регистра сведений, накладывался отбор по пяти измерениям, производилось чтение, удаление строки по индексу и повторная перезапись. Все это выполнялось внутри цикла, в котором еще и происходил повторный запрос к БД.

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

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


Процедура ОсновныеДействияФормыУдалить(Кнопка)
    
    // Флаг, который покажет, были ли произведены удаления в БД
    БылиИзменения = Ложь;
    
    // Начинаем транзакцию для ускорения массовой записи и целостности данных
    НачатьТранзакцию();
    
    Попытка
        Для Каждого СтрокаТЗ Из ТП1 Цикл  
            
            Если СтрокаТЗ.Отметка Тогда
                
                // Создаем пустой набор записей
                НаборЗаписей = РегистрыСведений.РезервРЦ.СоздатьНаборЗаписей();
                
                // Устанавливаем жесткие отборы по всем измерениям регистра
                НаборЗаписей.Отбор.Номенклатура.Установить(СтрокаТЗ.Номенклатура);
                НаборЗаписей.Отбор.Характеристика.Установить(СтрокаТЗ.Характеристика);
                НаборЗаписей.Отбор.ЕдиницаИзмерения.Установить(СтрокаТЗ.ЕдиницаИзмерения);
                НаборЗаписей.Отбор.ЗарезервированоЗа.Установить(СтрокаТЗ.ЗарезервированоЗа);
                НаборЗаписей.Отбор.ПоДокументу.Установить(СтрокаТЗ.ПоДокументу);
                
                // Записываем ПУСТОЙ набор с замещением (Истина) - это удалит записи из БД
                НаборЗаписей.Записать(Истина);
                
                БылиИзменения = Истина;
                
            КонецЕсли;
            
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение "Ошибка при удалении записей из регистра: " + ОписаниеОшибки();
    КонецПопытки;
    
    // Обновляем данные на форме ОДИН раз после завершения всех операций удаления
    Если БылиИзменения Тогда
        ОбновитьДанныеТаблицНаФорме();
    КонецЕсли;
    
КонецПроцедуры

Процедура ОбновитьДанныеТаблицНаФорме()
    
    // Повторно получаем актуальные данные для ТП1
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЛОЖЬ КАК Отметка,
    |	РезервРЦ.Номенклатура КАК Номенклатура,
    |	РезервРЦ.Характеристика КАК Характеристика,
    |	РезервРЦ.ЗарезервированоЗа КАК ЗарезервированоЗа,
    |	РезервРЦ.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    |	РезервРЦ.ПоДокументу КАК ПоДокументу,
    |	РезервРЦ.Количество КАК Количество
    |ИЗ
    |	РегистрСведений.РезервРЦ КАК РезервРЦ";
    
    ТП1 = Запрос.Выполнить().Выгрузить();
    ЭлементыФормы.ТП1.Значение = ТП1;
    
    // Не вызываем СоздатьКолонки(), чтобы не сбить визуальные настройки колонок-флажков!
    // Вместо этого просто обновляем строки на форме
    ЭлементыФормы.ТП1.ОбновитьСтроки();
    
КонецПроцедуры

Исправление функций массового выделения (Выбрать все / Снять все)

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

Также автор использовал метод СоздатьКолонки() для обновления формы, что ломало установленные свойства флажков. Выясним, как написать эти обработчики правильно, используя метод ОбновитьСтроки():


Процедура ВыбратьВсеНажатие(Кнопка)
    
    // Устанавливаем флаги для первой таблицы
    Для Каждого Строка Из ТП1 Цикл
        Строка.Отметка = Истина;
    КонецЦикла;
    ЭлементыФормы.ТП1.ОбновитьСтроки(); // Обновляем визуальное состояние без пересоздания колонок
    
    // Устанавливаем флаги для второй таблицы (исправлена ошибка обращения к ТП1)
    Для Каждого Строка Из ТП2 Цикл
        Строка.Отметка2 = Истина;
    КонецЦикла;
    ЭлементыФормы.ТП2.ОбновитьСтроки();
    
КонецПроцедуры

Процедура СнятьВсеНажатие(Кнопка)
    
    // Снимаем флаги для первой таблицы
    Для Каждого Строка Из ТП1 Цикл
        Строка.Отметка = Ложь;
    КонецЦикла;
    ЭлементыФормы.ТП1.ОбновитьСтроки();
    
    // Снимаем флаги для второй таблицы
    Для Каждого Строка Из ТП2 Цикл
        Строка.Отметка2 = Ложь;
    КонецЦикла;
    ЭлементыФормы.ТП2.ОбновитьСтроки();
    
КонецПроцедуры

Подведем итог: Использование встроенных возможностей платформы 1С:Предприятие для связывания данных (в данном случае свойства ДанныеФлажка) позволяет избавиться от 90% шаблонного интерфейсного кода. Это делает разработку более быстрой, конфигурацию — более производительной, а интерфейс программы — отзывчивым для конечного пользователя.

← На главную