При разработке интерфейсов на управляемых формах 1С программисты часто сталкиваются с задачей динамического изменения оформления. Типичный пример: пользователь выбирает счет учета в строке табличной части, и в зависимости от выбранного счета нам необходимо переименовать колонки субконто, чтобы они отражали реальные типы аналитики (например, «Контрагенты», «Договоры» вместо безликих «Субконто 1», «Субконто 2»).
Однако при попытке изменить свойство Заголовок колонки прямо во время редактирования строки происходит неприятный эффект: платформа принудительно завершает режим редактирования ячейки, закрывая активное поле ввода. В этой статье мы подробно разберем, почему это происходит, проанализируем возможные «костыли», их последствия и рассмотрим правильные архитектурные варианты решения этой задачи.
В управляемом интерфейсе «1С:Предприятия 8» за отображение элементов отвечает декларативная модель. Когда мы изменяем визуальные свойства элементов формы на клиенте (такие как Заголовок, Видимость, Доступность или положение колонки), платформа инициирует принудительную перерисовку (re-render) всего родительского элемента управления. В нашем случае этим элементом является таблица формы.
Поскольку таблица перестраивается заново для отображения нового заголовка, текущий интерактивный контекст редактирования строки сбрасывается. Платформа считает, что структура отображения данных изменилась, сохраняет текущие введенные данные во временный буфер строки и штатно завершает сессию редактирования. Избежать этой перерисовки при прямом изменении заголовка колонки технически невозможно — таков внутренний алгоритм работы движка управляемых форм.
На форумах часто предлагают быстрое решение: вернуть строку в режим редактирования программно сразу после изменения заголовка. Напрямую вызвать метод ИзменитьСтроку() в том же обработчике события ПриИзменении нельзя, так как событие еще не завершилось. Поэтому используют асинхронный вызов через обработчик ожидания.
Рассмотрим, как выглядит этот код:
&НаКлиенте
Процедура ТабличнаяЧасть1Реквизит1ПриИзменении(Элемент)
// Меняем заголовок колонки, что приводит к сбросу редактирования
Элементы.ТабличнаяЧасть1Реквизит2.Заголовок = "Новый заголовок";
// Подключаем однократный обработчик ожидания с минимальной задержкой
ПодключитьОбработчикОжидания("СменитьЗаголовокИВернутьРедактирование", 0.1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура СменитьЗаголовокИВернутьРедактирование()
// Программно возвращаем пользователя в режим редактирования строки
Элементы.ТабличнаяЧасть1.ИзменитьСтроку();
КонецПроцедуры
На первый взгляд, этот способ работает: заголовок меняется, а пользователь практически не замечает «мигания» интерфейса и продолжает ввод данных. Однако этот подход таит в себе серьезные проблемы, которые могут негативно сказаться на пользовательском опыте и стабильности системы.
Минусы использования метода ИзменитьСтроку():
Esc, все внесенные изменения в строке отменяются. Но когда мы вызываем ИзменитьСтроку() программно после смены заголовка, платформа фиксирует все изменения, сделанные ДО этого момента, и открывает новую транзакцию редактирования. В результате при нажатии Esc отменится только то, что было введено после вызова метода, а предыдущие изменения останутся в строке, что вводит пользователя в заблуждение.ПриАктивизацииСтроки или ПриАктивизацииЯчейки). В некоторых версиях платформы 1С это приводило к аварийному завершению работы тонкого клиента (вылетам системы без сохранения данных).Поскольку этот костыль создает больше проблем, чем решает, давайте рассмотрим профессиональные и архитектурно правильные способы реализации такой задачи.
Этот подход является стандартом проектирования интерфейсов в типовых конфигурациях фирмы «1С» (например, в «1С:Бухгалтерии» при редактировании аналитики счетов). Вместо того чтобы пытаться уместить сложную динамическую логику в ячейках таблицы, мы разделяем представление данных и их ввод.
Как это реализовать по шагам:
Элементы.ТабличнаяЧасть1.ТекущиеДанные.Субконто1.ПриАктивизацииСтроки (а также при изменении счета) мы программно меняем свойства Заголовок у полей ввода на этой панели.Посмотрим на пример кода управления заголовками на панели подробностей:
&НаКлиенте
Процедура ТабличнаяЧасть1ПриАктивизацииСтроки(Элемент)
ТекущиеДанные = Элемент.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Получаем реальные имена субконто для текущего счета (например, через кэшированный серверный вызов)
СтруктураИменСубконто = ПолучитьИменаСубконто(ТекущиеДанные.СчетУчета);
// Меняем заголовки полей ввода на панели под таблицей
Элементы.ПолеВводаСубконто1.Заголовок = СтруктураИменСубконто.Субконто1;
Элементы.ПолеВводаСубконто2.Заголовок = СтруктураИменСубконто.Субконто2;
КонецПроцедуры
При таком подходе сама таблица не перерисовывается, сессия редактирования строки не прерывается, а пользователь видит понятные и актуальные заголовки полей ввода на панели под таблицей. Это наиболее дружелюбный для пользователя интерфейс (UX).
Если по условиям задачи ввод данных обязательно должен происходить непосредственно в ячейках таблицы формы, мы можем использовать динамическое информирование пользователя без изменения структуры колонок.
Рассмотрим два варианта реализации:
ДекорацияИнформацияСубконто. В событии ПриАктивизацииСтроки или ПриИзменении счета обновляйте текст этой декорации: Элементы.ДекорацияИнформацияСубконто.Заголовок = "Редактируется субконто: " + ИмяСубконто;. Это не вызывает перерисовку таблицы.Подсказка или СверхбыстраяПодсказка для элемента поля ввода в колонке таблицы. При наведении курсора или при активизации ячейки пользователь будет видеть всплывающую подсказку с актуальным названием аналитики, при этом режим редактирования сбиваться не будет.Очень часто бизнес-логика документа подразумевает, что аналитика (например, счет учета) должна быть одинаковой для всех строк табличной части. В таком случае изменять заголовки колонок в момент редактирования конкретной строки — неверное решение.
Рекомендуемый сценарий:
ПриСозданииНаСервере на основе уже сохраненного в документе счета.Пример изменения заголовков при изменении реквизита шапки:
&НаКлиенте
Процедура ШапкаСчетУчетаПриИзменении(Элемент)
// Вызываем серверный метод для определения видов субконто
СтруктураСубконто = ПолучитьВидыСубконтоНаСервере(Объект.СчетУчета);
// Меняем заголовки колонок. Строки не редактируются, сброса фокуса нет
Элементы.ТабличнаяЧасть1Субконто1.Заголовок = СтруктураСубконто.Субконто1;
Элементы.ТабличнаяЧасть1Субконто2.Заголовок = СтруктураСубконто.Субконто2;
КонецПроцедуры
Если вариантов заголовков колонок относительно немного (например, у вас всего 3-4 возможных типа субконто), отличным решением будет создание отдельных колонок в конфигураторе под каждый тип аналитики с уже заданными статическими заголовками.
С помощью механизма Условного оформления или настройки видимости колонок при открытии формы мы можем показывать пользователю только те колонки, которые актуальны для текущего контекста документа, скрывая остальные. Обратите внимание, что управлять видимостью колонок во время активного редактирования строки также не стоит по той же причине перерисовки, но этот метод идеально подходит, если состав колонок определяется один раз при открытии документа.
Попытка обойти платформенное поведение 1С с помощью программного вызова ИзменитьСтроку() через обработчик ожидания — это жизнеспособный, но рискованный компромисс, который ухудшает эргономику формы (ломает клавишу Esc) и может приводить к нестабильности работы программы.
Для создания надежных и удобных интерфейсов мы рекомендуем использовать Панель детального редактирования под табличной частью или выносить управляющие реквизиты (такие как счета учета) в шапку формы, чтобы изменять заголовки колонок вне процесса интерактивного ввода строк.