При работе с обычными формами в 1С:Предприятии 8 начинающие разработчики часто сталкиваются с ситуацией, когда визуальные свойства элементов управления ведут себя непредсказуемо. Типичный пример — попытка настроить геометрические параметры колонок табличного поля (например, ширину) внутри событий отрисовки строк. В этой статье мы подробно разберем, почему событие ПриВыводеСтроки не подходит для изменения ширины колонок, как влияет на структуру табличного поля метод СоздатьКолонки() и как правильно управлять выравниванием текста на форме.
В исходном коде автора темы изменение ширины колонки «Разница» было помещено в обработчик события ДокументыПриВыводеСтроки:
Процедура ДокументыПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
Элемент.Колонки.Разница.Ширина = 2;
КонецПроцедуры
При прохождении кода отладчиком разработчик видит, что свойство Ширина действительно принимает значение 2, однако визуально на форме колонка не сжимается. Изменения применяются только в том случае, если пользователь вручную пытается изменить размер любой колонки мышью. Выясним причину такого поведения платформы.
Событие ПриВыводеСтроки срабатывает в процессе прорисовки (рендеринга) каждой отдельной видимой строки таблицы на экране. Оно предназначено исключительно для оформления конкретной строки через объект ОформлениеСтроки (например, для изменения цвета фона ячеек, шрифта или текста текущей строки). Изменение глобальных свойств самого элемента управления (таких как ширина колонки всего табличного поля) внутри процесса его отрисовки категорически запрещено архитектурой графической подсистемы платформы 1С.
Если бы платформа применяла изменение ширины всей колонки при отрисовке отдельной строки, это привело бы к необходимости полной перестройки геометрии таблицы (вызову процедуры Layout). Пересчет макета заново инициировал бы повторную отрисовку строк, что вызвало бы бесконечную рекурсию и полное зависание интерфейса программы. Чтобы избежать этого, 1С блокирует применение геометрических изменений во время выполнения обработчиков рисования. Изменения регистрируются в памяти, но не выводятся на экран до тех пор, пока внешнее событие (например, изменение размеров окна пользователем) не заставит таблицу принудительно обновить свой макет.
Второй важный аспект проблемы кроется в обработчике нажатия кнопки НоменклатураНажатие. В нем выполняется запрос, результаты которого выгружаются в таблицу значений, после чего вызывается метод:
ЭлементыФормы.Документы.СоздатьКолонки();
Рассмотрим подробнее работу этого метода. Метод СоздатьКолонки() полностью очищает существующую коллекцию колонок табличного поля и создает их заново на основе структуры источника данных (в данном случае — таблицы значений, полученной из запроса). При этом полностью сбрасываются все настройки внешнего вида, которые могли быть заданы интерактивно в конфигураторе или программно при открытии формы: ширина колонок, их заголовки, видимость, доступность и выравнивание.
Из этого следует важное правило разработки в 1С: любые программные настройки колонок табличного поля должны выполняться строго после вызова метода СоздатьКолонки().
Для того чтобы ширина колонки «Разница» корректно устанавливалась на форме, нам необходимо убрать некорректный код из события ПриВыводеСтроки и перенести его непосредственно в процедуру, где заполняются данные таблицы. Перенесем настройку ширины в самый конец обработчика НоменклатураНажатие, сразу после генерации колонок.
Проанализируем исправленный вариант процедуры:
Процедура НоменклатураНажатие(Кнопка, Элемент)
Номен = Истина;
Док = Ложь;
Запрос = Новый Запрос();
Запрос.Текст =
"ВЫБРАТЬ
| РасходнаяНакладнаяСостав.Номенклатура КАК Номенклатура,
| ЕСТЬNULL(ПриходнаяНакладнаяСостав.Количество, 0) - РасходнаяНакладнаяСостав.Количество КАК Разница,
| РасходнаяНакладнаяСостав.Ссылка КАК Документ
|ИЗ
| Документ.РасходнаяНакладная.Состав КАК РасходнаяНакладнаяСостав
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходнаяНакладная.Состав КАК ПриходнаяНакладнаяСостав
| ПО РасходнаяНакладнаяСостав.Номенклатура = ПриходнаяНакладнаяСостав.Номенклатура
| И РасходнаяНакладнаяСостав.Ссылка = ПриходнаяНакладнаяСостав.Ссылка.ДокОснование
|ГДЕ
| РасходнаяНакладнаяСостав.Количество - ЕСТЬNULL(ПриходнаяНакладнаяСостав.Количество, 0) <> 0
| И РасходнаяНакладнаяСостав.Ссылка.Контрагент ССЫЛКА Справочник.СтруктурныеЕдиницы
| И (&НеПроверятьДата ИЛИ РасходнаяНакладнаяСостав.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон)
| И (&НеПроверятьКонтрагент ИЛИ РасходнаяНакладнаяСостав.Ссылка.Контрагент = &СтруктурнаяЕдиница)
| И РасходнаяНакладнаяСостав.Ссылка.Проведен = ИСТИНА";
Если НЕ ПараметрыСеанса.ТекущаяСтруктурнаяЕдиница = Справочники.СтруктурныеЕдиницы.НайтиПоКоду("1") Тогда
ЭлементыФормы.Контрагент.Значение = ПараметрыСеанса.ТекущаяСтруктурнаяЕдиница;
ЭлементыФормы.Контрагент.Доступность = ЛОЖЬ;
КонецЕсли;
Запрос.УстановитьПараметр("ДатаНач", НачалоДня(ДатаНач));
Запрос.УстановитьПараметр("ДатаКон", КонецДня(ДатаКон));
Запрос.УстановитьПараметр("СтруктурнаяЕдиница", Контрагент);
Если Не ЗначениеЗаполнено(Контрагент) Тогда
Запрос.УстановитьПараметр("НеПроверятьКонтрагент", Истина);
Иначе
Запрос.УстановитьПараметр("НеПроверятьКонтрагент", Ложь);
КонецЕсли;
Если Не ЗначениеЗаполнено(ДатаНач) Тогда
Запрос.УстановитьПараметр("НеПроверятьДата", Истина);
Иначе
Запрос.УстановитьПараметр("НеПроверятьДата", Ложь);
КонецЕсли;
Если Не ЗначениеЗаполнено(ДатаКон) Тогда
Запрос.УстановитьПараметр("НеПроверятьДата", Истина);
Иначе
Запрос.УстановитьПараметр("НеПроверятьДата", Ложь);
КонецЕсли;
// Выгружаем данные в таблицу и привязываем к табличному полю
Документы = Запрос.Выполнить().Выгрузить();
// Создаем колонки заново на основе полученной таблицы значений
ЭлементыФормы.Документы.СоздатьКолонки();
// НАСТРОЙКА ОФОРМЛЕНИЯ: Настраиваем ширину конкретной колонки после пересоздания
КолонкаРазница = ЭлементыФормы.Документы.Колонки.Найти("Разница");
Если КолонкаРазница <> Неопределено Тогда
// Задаем ширину колонки (символьный размер)
КолонкаРазница.Ширина = 12;
КонецЕсли;
КонецПроцедуры
Обратите внимание: мы увеличили значение ширины колонки с экспериментального 2 до более реального значения 12, чтобы текст помещался целиком и не обрезался при выводе на форму.
Перейдем ко второму вопросу автора темы: как программно центрировать или сместить текст в колонке табличного поля. Для решения этой задачи у объекта КолонкаТабличногоПоля предусмотрено специальное свойство ГоризонтальноеПоложениеВКолонке. Оно принимает значения системного перечисления ГоризонтальноеПоложение.
Существует три независимых свойства выравнивания для разных частей колонки, которые мы можем настроить отдельно:
ГоризонтальноеПоложениеВКолонке — определяет выравнивание данных (текста, чисел) непосредственно в строках таблицы.ГоризонтальноеПоложениеВШапке — отвечает за положение текста заголовка колонки.ГоризонтальноеПоложениеВПодвале — регулирует выравнивание итоговых данных в подвале таблицы (если включена видимость подвала).Посмотрим на пример кода, который настраивает выравнивание колонки «Разница» по центру, а ее заголовок прижимает к левому краю:
КолонкаРазница = ЭлементыФормы.Документы.Колонки.Найти("Разница");
Если КолонкаРазница <> Неопределено Тогда
// Выравниваем числовые данные по центру
КолонкаРазница.ГоризонтальноеПоложениеВКолонке = ГоризонтальноеПоложение.Центр;
// Выравниваем текст в шапке по левому краю
КолонкаРазница.ГоризонтальноеПоложениеВШапке = ГоризонтальноеПоложение.Лево;
КонецЕсли;
Доступны следующие варианты выравнивания из перечисления ГоризонтальноеПоложение:
ГоризонтальноеПоложение.Лево — текст прижимается к левой границе ячейки.ГоризонтальноеПоложение.Центр — текст располагается строго по центру ячейки.ГоризонтальноеПоложение.Право — текст сдвигается к правой границе (стандартное поведение для чисел).ГоризонтальноеПоложение.Авто — автоматическое выравнивание в зависимости от типа данных (числа — вправо, строки — влево).Иногда требуется выравнивать текст выборочно, в зависимости от условий в конкретной строке. Например, положительные отклонения выравнивать по левому краю, а отрицательные — по правому. В этом случае общее свойство колонки нам не поможет. Нам придется использовать событие ПриВыводеСтроки, но изменять мы будем свойства не самой колонки, а конкретной оформляемой ячейки.
Разберем пример реализации такой логики:
Процедура ДокументыПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
// Получаем значение текущей ячейки
ТекущаяРазница = ДанныеСтроки.Разница;
// Находим нужную ячейку в оформлении строки
ЯчейкаРазница = ОформлениеСтроки.Ячейки.Разница;
Если ТекущаяРазница < 0 Тогда
// Отрицательные значения прижимаем вправо и выделяем красным цветом
ЯчейкаРазница.Выравнивание = ГоризонтальноеПоложение.Право;
ЯчейкаРазница.ЦветТекста = WebЦвета.Красный;
ИначеЕсли ТекущаяРазница > 0 Тогда
// Положительные значения выравниваем по центру и красим в зеленый
ЯчейкаРазница.Выравнивание = ГоризонтальноеПоложение.Центр;
ЯчейкаРазница.ЦветТекста = WebЦвета.ТемноЗеленый;
Иначе
// Нулевые значения оставляем серыми
ЯчейкаРазница.Выравнивание = ГоризонтальноеПоложение.Лево;
ЯчейкаРазница.ЦветТекста = WebЦвета.Серый;
КонецЕсли;
КонецПроцедуры
Свойство Выравнивание у объекта ОформлениеЯчейки имеет максимальный приоритет и перекрывает настройки, заданные глобально для всей колонки.
Поскольку автор темы упоминал попытки использовать событие ПриПолученииДанных, важно понимать принципиальную разницу между ними с точки зрения производительности системы.
Событие ПриВыводеСтроки вызывается для каждой отображаемой строки индивидуально. Если в списке видимых строк находится 30 элементов, событие сработает 30 раз подряд. Если внутри обработчика написать неоптимальный код (например, делать запросы к базе данных для получения дополнительной информации по текущей номенклатуре), интерфейс 1С начнет сильно «тормозить» при скроллинге таблицы.
Событие ПриПолученииДанных спроектировано более эффективно. Оно вызывается один раз для всей порции данных, которая сейчас отрисовывается на экране. В параметрах процедуры передается коллекция ОформленияСтрок, содержащая все видимые строки. Это позволяет разработчику выполнить всего один пакетный запрос к базе данных по всем строкам сразу (передав массив ссылок в качестве параметра запроса) и затем быстро распределить результаты по ячейкам таблицы. Рекомендуется использовать именно ПриПолученииДанных для любых задач, связанных со сложными расчетами или обращениями к СУБД в процессе отрисовки списков.
Если вы разрабатываете конфигурации на платформе 1С:Предприятие 8.3 с использованием Управляемых форм (тонкий и веб-клиент), вам следует помнить, что события ПриВыводеСтроки и ПриПолученииДанных там полностью отсутствуют. На управляемых формах все элементы интерфейса формируются декларативно на сервере.
Для реализации динамического изменения цвета, шрифтов, выравнивания или доступности ячеек используется механизм Условного оформления формы. Вы можете настроить правила оформления как интерактивно (в конфигураторе на вкладке «Условное оформление» формы), так и программно на сервере через коллекцию УсловноеОформление. Например, программное добавление правила условного оформления на управляемой форме выглядит так:
&НаСервере
Процедура НастроитьОформлениеНаСервере()
ЭлементОформления = УсловноеОформление.Элементы.Добавить();
// 1. Что оформляем (цвет текста)
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветТекста", ЦветаСтиля.ОсобыйТекст);
// 2. При каком условии (Разница < 0)
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.Документы.Разница");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Меньше;
ЭлементОтбора.ПравоеЗначение = 0;
// 3. Для какой колонки применять оформление
ОформляемоеПоле = ЭлементОформления.Поля.Элементы.Добавить();
ОформляемоеПоле.Поле = Новый ПолеКомпоновкиДанных("ДокументыРазница");
КонецПроцедуры
Этот способ работает на декларативном уровне, не нагружает клиентское приложение частыми вызовами кода и оптимально оптимизирован платформой для работы в сетевой среде.