При разработке интерфейсов для отправки SMS или систем с жестким ограничением на длину ввода программисты часто сталкиваются с проблемой: в платформе 1С (особенно версии 7.7) содержимое текстового поля попадает в связанную переменную только после того, как поле потеряет фокус. Это делает невозможным «живой» подсчет знаков стандартными средствами. В этой статье мы разберем, как обойти это ограничение, используя ActiveX, внешние компоненты и механизмы платформы 8.x.
Рассмотрим стандартную ситуацию: мы создаем реквизит формы «ТекстСообщения» и выводим его на форму. Если мы попробуем привязать к нему функцию для подсчета длины, мы обнаружим, что функция срабатывает только при нажатии Enter или переходе к другому элементу. Пока пользователь набирает текст, значение переменной ТекстСообщения остается старым. Проанализируем основные пути решения этой задачи в «семерке».
Одним из самых эффективных способов является замена стандартного текстового поля 1С на внешний элемент управления (ActiveX). Для этого используется библиотека FM20.DLL, которая обычно устанавливается вместе с Microsoft Office. Посмотрим на правильный способ создания такого элемента.
Важный нюанс, который часто упускают разработчики: при создании объекта через метод СоздатьЭУ() (если используется компонента FormEx) или штатные средства, необходимо указывать полную версию программного идентификатора (ProgID). Если написать просто "Forms.TextBox", система выдаст ошибку. Правильный идентификатор — Forms.TextBox.1.
// Пример создания объекта в модуле формы 1С 7.7
Процедура ПриОткрытии()
// Предполагаем, что на форме есть объект "АтрФормы" из FormEx
ТекстБокс = АтрФормы.СоздатьЭУ("Forms.TextBox.1", "ПолеВводаТекста");
// Теперь ТекстБокс доступен как объект ActiveX
КонецПроцедуры
У объекта Forms.TextBox.1 есть встроенное событие Change, которое срабатывает мгновенно при любом изменении текста. В отличие от стандартных полей 1С, это событие позволяет получать текст сразу. Выясним причину, почему это работает лучше: ActiveX-объекты работают в собственном потоке событий, и их свойства (например, Text) обновляются сразу после отрисовки символа операционной системой.
Если внедрение ActiveX кажется избыточным, можно воспользоваться популярной внешней компонентой FormEx (или 1C++). Эти библиотеки позволяют «подписаться» на системные сообщения Windows. Проанализируем ситуацию: когда пользователь нажимает клавишу, форма получает сообщение от операционной системы.
Разберем по шагам, какие события нам доступны:
ПриНажатииКнопкиКлавиатуры: Срабатывает в момент опускания клавиши. Особенность в том, что в этот микросекундный интервал символ еще не успевает добавиться в текстовый буфер поля ввода.ПриОтжатииКнопкиКлавиатуры: Это событие (KeyUp) более предпочтительно, так как к моменту его вызова символ уже фактически находится в поле ввода.Посмотрим на пример кода, который позволяет перехватить ввод:
Процедура ПриОтжатииКнопкиКлавиатуры(КодКлавиши, Флаги, ФлагСтандартнойОбработки)
// Метод работает через FormEx
// Получаем текущее значение прямо из окна (через WinAPI или свойства компоненты)
ТекущийТекст = Форма.АктивныйЭлемент().Текст;
Длина = СтрДлина(ТекущийТекст);
Форма.ЗаголовокСчетчика.Заголовок("Символов: " + Длина);
КонецПроцедуры
Стоит отметить, что для гарантированного получения текста без потери фокуса профессионалы иногда используют функцию SendMessage с параметром WM_GETTEXT. Это позволяет «вытащить» строку напрямую из памяти окна (Edit control), игнорируя механизмы привязки данных 1С.
Рассмотрим подробнее прикладную задачу, упомянутую в обсуждении, — подсчет символов для SMS. Здесь важно не просто знать СтрДлина(), а учитывать правила сегментации сообщений операторами. Разберем логику вычислений:
Проанализируем пример функции для такого расчета:
Функция РассчитатьКоличествоSMS(Текст)
Длина = СтрДлина(Текст);
// Проверка на наличие кириллицы (упрощенно)
ЕстьКириллица = 0;
Для н = 1 По Длина Цикл
Код = КодСимв(Сред(Текст, н, 1));
Если (Код > 127) Тогда ЕстьКириллица = 1; Прервать; КонецЕсли;
КонецЦикла;
ЛимитПервой = ?(ЕстьКириллица = 1, 70, 160);
ЛимитСегмента = ?(ЕстьКириллица = 1, 67, 153);
Если Длина <= ЛимитПервой Тогда
Возврат 1;
Иначе
Возврат Цел((Длина + ЛимитСегмента - 1) / ЛимитСегмента);
КонецЕсли;
КонецФункции
В современных версиях платформы (8.2 и 8.3) задача решается гораздо проще, так как разработчики платформы предусмотрели специальные события. Посмотрим на событие ИзменениеТекстаРедактирования. Оно специально предназначено для отслеживания ввода «на лету».
Рассмотрим, как это реализовать на клиенте:
&НаКлиенте
Процедура ТекстСообщенияИзменениеТекстаРедактирования(Элемент, Текст, СтандартнаяОбработка)
// Параметр "Текст" содержит то, что пользователь видит в данный момент
Длина = СтрДлина(Текст);
Элементы.НадписьСчетчик.Заголовок = "Введено символов: " + Строка(Длина);
// Можно также динамически менять цвет надписи, если лимит превышен
Если Длина > 160 Тогда
Элементы.НадписьСчетчик.ЦветТекста = ЦветаСтиля.ОсобыйТекст;
КонецЕсли;
КонецПроцедуры
Также в свойствах поля ввода 8.3 можно установить свойство «Обновление текста редактирования» в значение При вводе текста. Это заставит платформу обновлять связанную переменную реквизита чаще, чем при потере фокуса, однако событие ИзменениеТекстаРедактирования все равно остается самым быстрым и точным способом для реализации мгновенного счетчика.
Если вы не хотите использовать внешние компоненты в 7.7, можно использовать ОбработкаОжидания. Разберем этот алгоритм: мы запускаем процедуру, которая раз в 0.5 или 1 секунду проверяет состояние формы. Однако, как было замечено в обсуждении, этот метод имеет серьезный недостаток в 1С 7.7: при попытке программного обращения к тексту активного поля ввода система может сбросить позицию курсора или выделить весь текст, что мешает пользователю печатать. Поэтому данный метод крайне не рекомендуется для полей с активным вводом.
Подводя итог нашему анализу, можно выделить следующие рекомендации для разработчиков:
Forms.TextBox.1, если в системе гарантированно установлен Office, либо перехват событий клавиатуры через FormEx (событие ПриОтжатииКнопкиКлавиатуры).ИзменениеТекстаРедактирования, которое идеально подходит для этих целей.Используя эти методы, вы сможете создать современный и удобный интерфейс, который будет мгновенно реагировать на действия пользователя, обеспечивая качественный пользовательский опыт.