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