При разработке интерфейсов на управляемых формах в платформе «1С:Предприятие 8.3» программисты часто сталкиваются со специфическим поведением объекта СообщениеПользователю. Типичный сценарий: необходимо выполнить валидацию данных непосредственно в процессе редактирования строки табличной части (например, в событии ПриИзменении ячейки) и вывести пользователю красивую всплывающую подсказку (баллон) рядом с некорректно заполненным полем.
Однако на практике разработчики обнаруживают странное поведение: для первой строки табличной части сообщение отрабатывает идеально — ячейка подчеркивается красным, внизу экрана появляется текст ошибки, и вылетает всплывающий баллон-подсказка. Но стоит перейти к редактированию второй, третьей или последующих строк, как баллон перестает появляться, хотя текст ошибки внизу формы по-прежнему выводится, а рамка ячейки подсвечивается. В некоторых случаях баллон и вовсе «улетает» в шапку таблицы или позиционируется непредсказуемым образом.
В этой статье мы подробно проанализируем причины такого поведения платформы 1С, разберем типичные ошибки в кодовой базе и рассмотрим несколько надежных способов решения этой проблемы.
---
Основная и самая неочевидная причина, по которой всплывающий баллон-подсказка не отображается для последующих строк, кроется в механизме работы фокуса ввода управляемой формы.
Давайте проанализируем интерактивные действия пользователя. Когда пользователь редактирует значение в ячейке табличной части и нажимает клавишу Enter или Tab, происходят следующие события:
ПриИзменении.СообщениеПользователю.Платформа спроектирована так, что интерактивный баллон-подсказка физически может быть отображен только у того элемента формы, который в данный момент активен и находится в фокусе ввода. Когда мы пытаемся вывести сообщение для ячейки на строке №2, фокус ввода уже автоматически ушел на строку №3. Бизнес-логика платформы отрабатывает корректно: ячейка на строке №2 помечается как ошибочная (красное подчеркивание), а текст ошибки дублируется внизу экрана. Но так как фокус ввода уже находится на другой строке, платформа просто не может сопоставить всплывающий баллон с неактивным элементом и скрывает его, либо привязывает к заголовку всей колонки.
Почему же это работает для первой строки? Если в таблице присутствует всего одна строка, то при нажатии клавиши ввода фокус физически не может перейти на следующую строку. Он остается в пределах текущей строки, и баллон успешно позиционируется.
---
Обратим внимание на код, который часто используют начинающие разработчики:
ТекущаяСтрока = Элементы.ТаблицаПоказателей.ТекущиеДанные;
Индекс = Объект.ТаблицаПоказателей.Индекс(ТекущаяСтрока);
Сообщение.Поле = "Объект.ТаблицаПоказателей[" + Индекс + "].План";
Здесь кроется серьезная методологическая ошибка. Свойство ТекущаяСтрока табличного поля на клиенте возвращает не индекс строки в коллекции и не саму строку, а её уникальный идентификатор (числовое значение, присвоенное платформой). Метод Индекс() коллекции табличной части на клиенте может работать нестабильно или возвращать неверные значения, если строки динамически удалялись, перемещались или сортировались пользователем.
Если индекс определен неверно, платформа не сможет сопоставить путь, указанный в свойстве Поле, с реальным элементом на форме, и сообщение не привяжется к ячейке.
---
Когда мы формируем свойство Поле на клиенте в виде единой строки "Объект.ТаблицаПоказателей[0].План", платформа вынуждена самостоятельно парсить эту строку, чтобы определить, к какому именно реквизиту формы относится сообщение. В контексте клиентского вызова без явного указания основного источника данных (свойства ПутьКДанным) эта процедура часто дает сбои.
Кроме того, при неявном преобразовании индекса строки в строку (например, при сложении числа со строкой) платформа применяет локальные настройки форматирования чисел. Если индекс строки превышает 999 (например, строка 1000), число преобразуется в строку с разделителем групп разрядов (неразрывным пробелом): "1 000". В результате путь к данным принимает вид "Объект.ТаблицаПоказателей[1 000].План", что является синтаксически некорректным для платформы, и привязка сообщения полностью ломается.
---
Рассмотрим подробнее, как исправить эти ошибки и заставить баллон-подсказку стабильно выводиться для любой строки табличной части.
Чтобы баллон гарантированно отобразился на нужной строке, мы должны выполнить два условия: правильно составить путь к данным с использованием ПутьКДанным и принудительно вернуть фокус ввода на ту строку, где произошла ошибка.
Разберем рабочий пример кода на клиенте:
&НаКлиенте
Процедура ТаблицаПоказателейПланПриИзменении(Элемент)
ТекущиеДанные = Элементы.ТаблицаПоказателей.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Выполняем проверку бизнес-логики
Если ТекущиеДанные.План < 0 Тогда
// 1. Принудительно возвращаем фокус на строку с ошибкой
// Используем идентификатор строки, полученный через метод ПолучитьИдентификатор()
ИдентификаторСтроки = ТекущиеДанные.ПолучитьИдентификатор();
Элементы.ТаблицаПоказателей.ТекущаяСтрока = ИдентификаторСтроки;
// 2. Получаем правильный индекс строки в коллекции данных
ИндексСтроки = Объект.ТаблицаПоказателей.Индекс(ТекущиеДанные);
// 3. Формируем сообщение пользователю с явным разделением путей
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Значение плана не может быть отрицательным!";
// Указываем основной реквизит формы в ПутьКДанным
Сообщение.ПутьКДанным = "Объект";
// В свойстве Поле пишем относительный путь, ОБЯЗАТЕЛЬНО форматируя индекс без пробелов
Сообщение.Поле = "ТаблицаПоказателей[" + Формат(ИндексСтроки, "ЧГ=0") + "].План";
// Выводим сообщение
Сообщение.Сообщить();
КонецЕсли;
КонецПроцедуры
В этом примере мы использовали метод Формат(ИндексСтроки, "ЧГ=0"), что полностью исключает появление неразрывных пробелов в индексе строки при любых региональных настройках системы. Установка свойства ТекущаяСтрока возвращает фокус обратно на редактируемую строку, благодаря чему платформа успешно позиционирует баллон-подсказку у нужной ячейки.
---
Вызов СообщениеПользователю в интерактивных событиях элементов управления (таких как ПриИзменении) часто считается архитектурным антипаттерном. Механизм сообщений проектировался в первую очередь для валидации данных при записи или проведении документа, когда фокус ввода заблокирован платформой.
Если бизнес-логика позволяет выполнять проверку не «на лету», а непосредственно перед сохранением документа, лучшим решением будет перенос проверок в стандартный обработчик модуля объекта.
Рассмотрим пример реализации в модуле документа:
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// Обходим табличную часть в цикле
Для Каждого СтрокаТЧ Из ТаблицаПоказателей Цикл
Если СтрокаТЧ.План < 0 Тогда
ИндексСтроки = ТаблицаПоказателей.Индекс(СтрокаТЧ);
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "В строке №" + (ИндексСтроки + 1) + " значение плана не может быть отрицательным!";
Сообщение.Поле = "ТаблицаПоказателей[" + Формат(ИндексСтроки, "ЧГ=0") + "].План";
Сообщение.УстановитьДанные(ЭтотОбъект); // Автоматически связывает сообщение с объектом
Сообщение.Сообщить();
Отказ = Истина; // Блокируем запись документа
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При таком подходе платформа берет на себя все заботы по управлению фокусом. При попытке записать документ с некорректными данными запись будет отменена, а пользователь увидит системное окно со списком ошибок. При клике на любую ошибку в списке платформа сама переведет фокус на нужную строку табличной части и откроет всплывающий баллон.
---
Если проверка не является критичной для проведения документа и вам нужно просто предупредить пользователя о неоптимальном вводе, не ломая при этом фокус и не раздражая его красными подчеркиваниями, лучше отказаться от СообщениеПользователю в пользу всплывающих системных оповещений.
Посмотрим, как это реализовать:
&НаКлиенте
Процедура ТаблицаПоказателейПланПриИзменении(Элемент)
ТекущиеДанные = Элементы.ТаблицаПоказателей.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
Если ТекущиеДанные.План > 1000000 Тогда
// Используем неблокирующее оповещение в правом нижнем углу экрана
ПоказатьОповещениеПользователя(
"Внимание! Введено крупное значение", // Заголовок
, // Действие при клике
"Значение плана превышает 1 000 000. Пожалуйста, убедитесь в корректности ввода.", // Текст
БиблиотекаКартинок.Предупреждение // Картинка
);
КонецЕсли;
КонецПроцедуры
Такой способ гарантирует, что интерфейс программы будет работать плавно, без мерцаний экрана при принудительной смене фокуса, а пользователь оперативно получит необходимую информацию.
---
Для того чтобы всплывающие сообщения в табличной части работали стабильно, необходимо соблюдать три золотых правила разработки на управляемых формах:
Элементы.ИмяТаблицы.ТекущаяСтрока.ПутьКДанным (указывайте контекст, например, "Объект") и относительное Поле.Формат(Индекс, "ЧГ=0") при динамическом конструировании путей к элементам коллекций.