Как определить изменение текста в поле ввода неограниченной длины в 1С 7.7 до потери фокуса?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

В платформе 1С 7.7 существует давняя проблема: значение переменной, связанной с полем ввода (особенно многострочным), обновляется только в тот момент, когда элемент теряет фокус (событие KillFocus). Если пользователь активно редактирует текст, но не переходит на другой реквизит, программа «не видит» изменений. Это создает сложности, например, когда нужно визуально показать пользователю, что данные изменены, или запретить закрытие формы без сохранения.

В данной статье мы проанализируем ситуацию и разберем несколько способов решения этой задачи: от простых штатных обходов до использования внешних компонент и низкоуровневых системных вызовов.

Причина проблемы: особенности движка 1С 7.7

Рассмотрим, почему стандартные методы не работают. В 1С 7.7 механизм привязки данных (Data Binding) работает дискретно. Поле ввода на форме — это стандартный Windows-контрол Edit. Когда пользователь печатает, текст меняется внутри контрола, но 1С узнает об этом только тогда, когда управление передается другому элементу. Попытка прочитать переменную в процессе ввода вернет старое значение, которое было в ней до начала редактирования.

Решение 1: Перехват событий клавиатуры через FormEx

Если в конфигурации используется внешняя компонента FormEx, мы можем попытаться отловить каждое нажатие клавиши в реальном времени. Это позволяет мгновенно реагировать на ввод пользователя.

Разберем пример кода, который можно разместить в модуле формы:


Процедура ПриОтжатииКнопкиКлавиатуры(Код, Альт, Шифт, Контрол, Символ, ФСО)
    // Проверяем, что фокус находится на нужном нам поле
    Если Форма.АктивныйЭлемент() <> "ОписаниеТовара" Тогда 
        Возврат; 
    КонецЕсли;
    
    // Игнорируем управляющие клавиши (курсоры, функциональные и т.д.), если нужно
    // Код > 32 — это печатные символы
    Если Код > 32 Тогда
        флТекстИзменен = 1;
        Форма.кнЗакрыть.Заголовок("&Закрыть ( ИЗМЕНЕНИЯ НЕ СОХРАНЕНЫ )");
        // Чтобы заголовок кнопки обновился визуально, иногда требуется обновление формы
        РасширениеФормы.Обновить(); 
    КонецЕсли;
КонецПроцедуры

Важный нюанс: использование РасширениеФормы.Обновить() или Форма.Обновить() после каждого нажатия клавиши может привести к неприятному «морганию» экрана. Чтобы минимизировать этот эффект, рекомендуется обновлять форму только один раз при фиксации первого изменения.

Решение 2: Использование скрытой формулы

Проанализируем «хитрый» штатный способ. Мы можем поместить на форму текстовый элемент (не поле ввода!), сделать его невидимым или очень маленьким и в поле «Формула» прописать вызов функции.

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

Решение 3: Отслеживание через Windows API (SendMessage)

Это наиболее профессиональный и надежный метод. Каждое окно в Windows (включая поле ввода 1С) может отвечать на системные сообщения. Мы можем отправить сообщение WM_GETTEXT непосредственно дескриптору (hWnd) поля ввода. Это позволит получить текущий текст из «экранного» элемента, даже если 1С еще не обновила свою внутреннюю переменную.

Для реализации этого метода через FormEx или 1CPP нам потребуется:

  1. Получить hWnd (хендл) нужного поля ввода.
  2. Использовать функцию SendMessage.
  3. Сравнить полученный результат с исходным текстом.

Посмотрим на логику (псевдокод):


// Пример получения текста через WinAPI
ТекстИзПоля = "";
hWndПоля = РасширениеФормы.ПолучитьАтрибут("ОписаниеТовара").hWnd;
ТекстИзПоля = WinAPI_SendMessage_GetText(hWndПоля); 

Если ТекстИзПоля <> ИсходныйТекст Тогда
    флТекстИзменен = 1;
КонецЕсли;

Преимущество этого способа в том, что он позволяет отловить не только ввод с клавиатуры, но и вставку текста мышкой (через контекстное меню «Вставить»), что невозможно сделать через ПриНажатииКнопкиКлавиатуры.

Решение 4: Использование обработки ожидания

Если мгновенная реакция не критична, можно запустить периодическую проверку. Рассмотрим по шагам:

  1. При открытии формы запоминаем текущее значение текста в переменную ИсходныйТекст.
  2. Запускаем Форма.ОбработкаОжидания("ПроверитьИзменения", 1).
  3. В функции ПроверитьИзменения сравниваем текущее состояние.

Внимание: Чтобы этот метод сработал без увода фокуса, нам снова придется использовать SendMessage, описанный в предыдущем пункте. Если просто сравнивать переменную 1С, результат будет нулевым до момента перехода на другое поле.

Решение 5: Использование альтернативных элементов управления

Если штатное поле ввода накладывает слишком много ограничений, программисты 1С 7.7 часто прибегают к использованию внешних компонент, которые ведут себя более гибко:

Решение 6: Компромиссный вариант (Проверка при закрытии)

Выясним ситуацию, когда «красивое» решение в реальном времени слишком трудозатратно. Можно реализовать проверку в момент закрытия формы или при нажатии кнопки «Закрыть».

Рассмотрим алгоритм, предложенный автором темы:

  1. При открытии формы сохраняем текст в ТекстПриОткрытии.
  2. В процедуре кнопки «Закрыть» принудительно переводим фокус на саму кнопку (это заставляет 1С обновить значение переменной поля ввода).
  3. Сравниваем ТекстВПоле и ТекстПриОткрытии.
  4. Если они не равны — задаем вопрос пользователю: «Данные изменены. Сохранить?»

Процедура ПриНажатииКнопкиЗакрыть()
    // Переводим фокус, чтобы обновить значение переменной ОписаниеТовара
    Форма.кнЗакрыть.УстановитьФокус();
    
    Если ОписаниеТовара <> ТекстПриОткрытии Тогда
        Ответ = Вопрос("Текст был изменен. Сохранить изменения?", "Да+Нет+Отмена");
        Если Ответ = "Да" Тогда
            СохранитьДанные();
            СтатусВозврата(1);
        ИначеЕсли Ответ = "Нет" Тогда
            СтатусВозврата(1);
        Иначе
            СтатусВозврата(0); // Остаемся в форме
        КонецЕсли;
    Иначе
        СтатусВозврата(1);
    КонецЕсли;
КонецПроцедуры

Подведение итогов

Мы проанализировали различные подходы к определению модифицированности текста. Подведем итог:

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

← На главную