Как программно установить курсор в конец строки в поле ввода 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов в 1С:Предприятии часто возникает задача повышения удобства ввода данных (UX). Представим ситуацию: пользователь активно вводит текст, в котором часто используется определенный символ (например, решетка # для тегов, двоеточие или специфический разделитель). Чтобы избавить пользователя от постоянного переключения раскладки клавиатуры, мы выносим на форму специальную кнопку, которая добавляет нужный символ в текст.

Однако здесь мы сталкиваемся с неприятным поведением платформы: при нажатии на кнопку фокус ввода (активность) уходит с текстового поля. Если мы принудительно возвращаем фокус с помощью конструкции ЭтаФорма.ТекущийЭлемент = Элементы.ТекстОписания;, платформа по умолчанию помещает курсор в самое начало строки или выделяет весь текст целиком. Пользователю приходится вручную кликать мышкой или нажимать клавиши на клавиатуре, чтобы продолжить ввод.

В этой статье мы подробно рассмотрим, как решить эту проблему, и разберем все доступные способы программного переноса курсора в конец строки в управляемых формах 1С.

Причина поведения платформы

Прежде чем перейти к коду, давайте выясним причину такого поведения системы. Когда мы переносим фокус на поле ввода программно через свойство ТекущийЭлемент, платформа 1С воспринимает это как "вход" в поле. В зависимости от настроек элемента и операционной системы, платформа инициализирует режим редактирования стандартным образом: либо выделяет весь существующий текст, либо сбрасывает позицию каретки (курсора) на индекс 0 (начало строки).

Для обхода этой особенности нам потребуется принудительно управлять границами выделения текста в элементе формы.

Способ 1. Использование метода УстановитьГраницыВыделения()

Это самый правильный, надежный и кроссплатформенный способ, встроенный непосредственно в платформу 1С:Предприятие. У элементов формы типа "Поле ввода" (содержащих текстовые данные) существует штатный метод УстановитьГраницыВыделения().

Синтаксис метода следующий:

УстановитьГраницыВыделения(<Начало>, <Конец>)

Если мы передадим в оба параметра одинаковое число, равное длине строки плюс один, то выделения текста не произойдет. Вместо этого система просто установит каретку (курсор) в указанную позицию — то есть в самый конец нашей строки.

Давайте разберем по шагам практический пример реализации этого метода:


&НаКлиенте
Процедура ДобавитьСимволРешетки(Команда)
    
    // Шаг 1. Добавляем символ в реквизит, связанный с полем ввода
    Объект.ТекстОписания = Объект.ТекстОписания + "#";
    
    // Шаг 2. Возвращаем фокус на поле ввода
    ЭтаФорма.ТекущийЭлемент = Элементы.ТекстОписания;
    
    // Шаг 3. Вычисляем позицию для курсора
    // Длина строки плюс 1 дает нам позицию сразу за последним символом
    ПозицияКурсора = СтрДлина(Объект.ТекстОписания) + 1;
    
    // Шаг 4. Устанавливаем курсор в конец
    Элементы.ТекстОписания.УстановитьГраницыВыделения(ПозицияКурсора, ПозицияКурсора);
    
КонецПроцедуры

Важное примечание: Если пользователь вводит текст, но данные еще не были сохранены или записаны в реквизит, более надежным решением будет получение длины текста непосредственно из визуального редактора формы с помощью свойства ТекстРедактирования:

ПозицияКурсора = СтрДлина(Элементы.ТекстОписания.ТекстРедактирования) + 1;

Способ 2. Обновление отображения перед установкой курсора

Иногда при работе в тонком или веб-клиенте можно заметить, что метод УстановитьГраницыВыделения() отрабатывает некорректно или курсор все равно "прыгает" в начало. Это происходит из-за рассинхронизации данных: платформа не успевает обновить визуальное представление текстового поля на экране после того, как мы программно изменили связанный реквизит Объект.ТекстОписания.

Чтобы гарантировать точный результат, мы должны принудительно заставить форму обновить отображение текстового поля перед позиционированием курсора. Для этого используется метод ОбновитьТекстРедактирования():


&НаКлиенте
Процедура ДобавитьСимволНадежно(Команда)
    
    Объект.ТекстОписания = Объект.ТекстОписания + "#";
    ЭтаФорма.ТекущийЭлемент = Элементы.ТекстОписания;
    
    // Принудительно синхронизируем данные реквизита с экранной формой
    Элементы.ТекстОписания.ОбновитьТекстРедактирования();
    
    Позиция = СтрДлина(Объект.ТекстОписания) + 1;
    Элементы.ТекстОписания.УстановитьГраницыВыделения(Позиция, Позиция);
    
КонецПроцедуры

Способ 3. Использование свойства ВыделенныйТекст

Рассмотрим еще один интересный и очень элегантный прием, основанный на поведении свойства ВыделенныйТекст. В 1С запись значения в это свойство не только заменяет выделенный фрагмент текста (или вставляет его в позицию курсора, если ничего не выделено), но и автоматически переносит курсор в самый конец вставленного текста.

Используя эту логику, мы можем построить следующий алгоритм:

  1. Устанавливаем фокус на наше текстовое поле.
  2. Переносим выделение (пустое) в конец существующего текста.
  3. Присваиваем свойству ВыделенныйТекст наш символ #.

Посмотрим на программную реализацию этой схемы:


&НаКлиенте
Процедура ДобавитьСимволЧерезВыделение(Команда)
    
    // Фокусируем элемент на форме
    ЭтаФорма.ТекущийЭлемент = Элементы.ТекстОписания;
    
    // Вычисляем конец строки
    Позиция = СтрДлина(Объект.ТекстОписания) + 1;
    
    // Сдвигаем курсор в конец без выделения текста
    Элементы.ТекстОписания.УстановитьГраницыВыделения(Позиция, Позиция);
    
    // Вставляем символ в позицию курсора. 
    // Платформа сама сдвинет курсор вправо за вставленный символ!
    Элементы.ТекстОписания.ВыделенныйТекст = "#";
    
КонецПроцедуры

Способ 4. Обход ограничений через обработчик ожидания

В некоторых версиях платформы 1С:Предприятие (особенно в веб-клиенте под управлением некоторых браузеров) наблюдается неприятный эффект: встроенные механизмы браузера или платформы выполняют сброс выделения и фокуса уже после завершения работы процедуры кнопки. То есть наш код успешно отрабатывает, переносит курсор в конец, но как только управление возвращается платформе, она выполняет стандартное событие фокуса и сбрасывает курсор в начало строки.

Для обхода этой проблемы мы можем применить асинхронный вызов. Мы отложим установку курсора на минимальное время (например, на 0.1 секунды) с помощью обработчика ожидания. За это время платформа завершит свои стандартные процедуры фокусировки, а наш вызванный следом код установит курсор туда, куда нужно.

Проанализируем этот алгоритм на примере:


&НаКлиенте
Процедура ДобавитьСимволСЗадержкой(Команда)
    
    // Добавляем символ
    Объект.ТекстОписания = Объект.ТекстОписания + "#";
    
    // Фокусируем элемент
    ЭтаФорма.ТекущийЭлемент = Элементы.ТекстОписания;
    
    // Подключаем однократный обработчик ожидания с задержкой 0.1 сек
    ПодключитьОбработчикОжидания("УстановитьКурсорВКонецСтрокиАсинхронно", 0.1, Истина);
    
КонецПроцедуры

&НаКлиенте
Процедура УстановитьКурсорВКонецСтрокиАсинхронно()
    
    Позиция = СтрДлина(Объект.ТекстОписания) + 1;
    Элементы.ТекстОписания.УстановитьГраницыВыделения(Позиция, Позиция);
    
КонецПроцедуры

Способ 5. Legacy-метод эмуляции нажатия клавиш (только для Windows)

В старых конфигурациях или при работе в толстом клиенте под управлением ОС Windows иногда использовали отправку горячих клавиш через COM-объект операционной системы. Этот метод был предложен на форуме (сообщение 4).

Идея заключается в том, чтобы после фокусировки на поле ввода сымитировать нажатие клавиши END на клавиатуре с помощью скрипта WScript.Shell:


&НаКлиенте
Процедура ДобавитьСимволЧерезКлавиатуру(Команда)
    
    Объект.ТекстОписания = Объект.ТекстОписания + "#";
    ЭтаФорма.ТекущийЭлемент = Элементы.ТекстОписания;
    
    Попытка
        // Создаем COM-объект операционной системы Windows
        Shell = Новый COMОбъект("WScript.Shell");
        // Отправляем нажатие клавиши END для перехода в конец строки
        Shell.sendKeys("{END}");
    Исключение
        // Метод не сработает на Linux, macOS или в Веб-клиенте
        ЗаписьЖурналаРегистрации("ОшибкаЭмуляцииВвода", УровеньЖурналаРегистрации.Предупреждение);
    КонецПопытки;
    
КонецПроцедуры

Важное предупреждение: Мы крайне не рекомендуем использовать этот способ в современной разработке. Он обладает критическими недостатками:

Резюме и рекомендации

Мы рассмотрели различные варианты решения проблемы с позиционированием курсора в 1С. Подведем итоги:

  1. Для большинства стандартных задач на управляемых формах оптимальным и наиболее стабильным является Способ 1 с использованием встроенного метода УстановитьГраницыВыделения().
  2. Если вы сталкиваетесь со странным поведением платформы или "залипанием" курсора в начале строки, используйте связку с принудительным обновлением данных (Способ 2) или асинхронный вызов через обработчик ожидания (Способ 4).
  3. Избегайте использования внешних COM-объектов Windows (Способ 5), чтобы ваше решение оставалось кроссплатформенным и безопасным в любых режимах работы платформы "1С:Предприятие".
← На главную