При разработке интерфейсов в 1С:Предприятии часто возникает необходимость упростить ввод данных для пользователя. Один из классических сценариев — использование масок ввода для сложных кодов, артикулов или номеров документов (например, 9999/99.99.9999/99999). Однако при попытке сделать поле «умным» и автоматически сдвигать курсор в начало определенной группы маски при клике разработчики сталкиваются с серьезными ограничениями платформы.
В этой статье мы подробно разберем, почему стандартными средствами платформы 1С сложно «поймать» позицию курсора в момент клика, проанализируем поведение функций работы с выделением текста и рассмотрим три практических способа решения этой задачи.
Главная проблема кроется в событийной модели элементов управления 1С. Для стандартного элемента «Поле ввода» на управляемой форме отсутствуют такие низкоуровневые события, как:
ПриКлике (OnClick) — событие нажатия кнопкой мыши внутри текстового поля;ПриИзмененииПоложенияКурсора — событие, которое срабатывало бы каждый раз, когда каретка перемещается по тексту.Событие ПриАктивизации относится к элементам таблиц, а события типа АвтоПодборТекста или ОкончаниеВводаТекста реагируют только на изменение содержимого поля, но никак не на перемещение курсора пользователем с помощью мыши.
Таким образом, мы не можем в реальном времени стандартным образом узнать, в какую именно точку поля ввода кликнул пользователь, чтобы мгновенно перепозиционировать каретку.
Давайте подробно рассмотрим, как работают функции позиционирования курсора и какие обходные пути (workarounds) существуют для реализации этой задачи.
Для программного управления выделением и позицией курсора в 1С используются два ключевых метода элемента формы. Рассмотрим их синтаксис:
ПолучитьГраницыВыделения(НачалоВыделения, КонецВыделения) — считывает текущую позицию курсора или границы выделенного фрагмента текста. Если текст не выделен, а курсор просто стоит в поле, переменные НачалоВыделения и КонецВыделения вернут одинаковое число (индекс символа, начиная с 1).УстановитьГраницыВыделения(НачалоВыделения, КонецВыделения) — устанавливает фокус и выделяет фрагмент текста. Чтобы просто установить курсор в нужную позицию без выделения текста, достаточно передать одинаковое значение в оба параметра: УстановитьГраницыВыделения(Позиция, Позиция).Проанализируем ограничения при совместном использовании этих методов с маской ввода. Когда на поле ввода наложена жесткая маска (например, @9999/99.99.9999/99999), платформа 1С берет на себя строгий контроль за вводом символов. Попытки принудительно изменить границы выделения во время интерактивного ввода часто приводят к тому, что платформа сбрасывает курсор в начало или конец поля, игнорируя программные настройки.
Если мы проанализируем поведение интерфейса с точки зрения удобства пользователя, то принудительный сдвиг курсора внутри одного поля часто вызывает раздражение. Представьте ситуацию: пользователь хочет исправить одну опечатку во второй группе цифр, кликает по ней, а программа настойчиво перекидывает его курсор в самое начало этой группы.
Гораздо более изящным и простым в реализации решением является разделение одного сложного поля на три отдельных поля ввода, визуально оформленных как единое целое. Для этого мы убираем общую маску и создаем три поля:
ПолеГруппа1 (маска: 9999)ПолеГруппа2 (маска: 99.99.9999)ПолеГруппа3 (маска: 99999)На форме эти поля располагаются горизонтально в одной группе без отступов, а между ними размещаются декоративные надписи-разделители "/". Чтобы сделать ввод бесшовным, настроим автоматический переход фокуса между полями при достижении максимальной длины ввода.
Разберем по шагам код обработчиков событий для автоперехода:
&НаКлиенте
Процедура ПолеГруппа1ПриИзменении(Элемент)
// Как только введены все 4 символа первой группы, перекидываем фокус на вторую
Если СтрДлина(Объект.ПолеГруппа1) = 4 Тогда
ЭтаФорма.ТекущийЭлемент = Элементы.ПолеГруппа2;
// Устанавливаем курсор в самую первую позицию второго поля
Элементы.ПолеГруппа2.УстановитьГраницыВыделения(1, 1);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПолеГруппа2ПриИзменении(Элемент)
// Длина маски второй группы "99.99.9999" составляет 10 символов с учетом точек
Если СтрДлина(Объект.ПолеГруппа2) = 10 Тогда
ЭтаФорма.ТекущийЭлемент = Элементы.ПолеГруппа3;
Элементы.ПолеГруппа3.УстановитьГраницыВыделения(1, 1);
КонецЕсли;
КонецПроцедуры
Этот способ полностью решает проблему позиционирования: при клике на конкретную группу пользователь гарантированно попадает именно в то поле, которое его интересует, а при последовательном вводе переход происходит автоматически.
Если по требованиям технического задания поле ввода обязательно должно оставаться единым целым, мы можем применить обходной путь с использованием таймера фонового опроса. Этот метод основан на периодической проверке активного элемента формы и позиции курсора в нем.
Разберем алгоритм настройки обработчика ожидания:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Подключаем обработчик с интервалом 0.1 секунды (100 миллисекунд)
ПодключитьОбработчикОжидания("ПроверитьИПозиционироватьКурсор", 0.1, Ложь);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьИПозиционироватьКурсор()
ТекущийЭлементФормы = ЭтаФорма.ТекущийЭлемент;
// Проверяем, что фокус находится именно на нашем маскированном поле
Если ТекущийЭлементФормы = Элементы.НомерОперации Тогда
НачалоВыделения = 0;
КонецВыделения = 0;
// Получаем текущую позицию курсора
ТекущийЭлементФормы.ПолучитьГраницыВыделения(НачалоВыделения, КонецВыделения);
// Наша маска: 9999/99.99.9999/99999
// Индексы символов разделителей '/': 5 и 16
// Первая группа: 1-4 символы
// Вторая группа: 6-15 символы
// Третья группа: 17-21 символы
Если НачалоВыделения > 5 И НачалоВыделения < 16 Тогда
// Пользователь кликнул во вторую группу.
// Если курсор стоит не в начале группы (позиция 6), принудительно сдвигаем его туда
Если НачалоВыделения <> 6 Тогда
ТекущийЭлементФормы.УстановитьГраницыВыделения(6, 6);
КонецЕсли;
ИначеЕсли НачалоВыделения > 16 Тогда
// Пользователь кликнул в третью группу.
// Если курсор стоит не в начале группы (позиция 17), сдвигаем его туда
Если НачалоВыделения <> 17 Тогда
ТекущийЭлементФормы.УстановитьГраницыВыделения(17, 17);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важное предупреждение: Использование фоновых таймеров с высокой частотой опроса (0.1 сек) создает дополнительную вычислительную нагрузку на клиентский сеанс. Кроме того, у пользователя может возникнуть визуальный эффект «прыгающего» курсора, так как каретка на долю секунды отобразится в месте клика и лишь затем переместится в начало группы.
Для веб-клиента и конфигураций со сложным UX наиболее технологичным решением является перенос логики ввода в поле HTML-документа. Вместо стандартного поля ввода 1С мы можем отобразить HTML-страницу с элементом <input>, к которому подключена специализированная библиотека маскирования на JavaScript (например, IMask или jQuery Mask Plugin).
Преимущества данного подхода:
onclick, onfocus и onkeydown внутри HTML-элемента;selectionStart и selectionEnd без задержек и «дерганий»;последовательностьСтрокиСобытия или отправка данных через веб-клиент).Мы проанализировали три принципиально разных подхода к реализации позиционирования курсора в маскированном поле ввода 1С. Выбор конкретного метода зависит от требований к интерфейсу вашего прикладного решения и готовности усложнять программный код ради удобства пользователей.