В платформе 1С 7.7 существует давняя проблема: значение переменной, связанной с полем ввода (особенно многострочным), обновляется только в тот момент, когда элемент теряет фокус (событие KillFocus). Если пользователь активно редактирует текст, но не переходит на другой реквизит, программа «не видит» изменений. Это создает сложности, например, когда нужно визуально показать пользователю, что данные изменены, или запретить закрытие формы без сохранения.
В данной статье мы проанализируем ситуацию и разберем несколько способов решения этой задачи: от простых штатных обходов до использования внешних компонент и низкоуровневых системных вызовов.
Рассмотрим, почему стандартные методы не работают. В 1С 7.7 механизм привязки данных (Data Binding) работает дискретно. Поле ввода на форме — это стандартный Windows-контрол Edit. Когда пользователь печатает, текст меняется внутри контрола, но 1С узнает об этом только тогда, когда управление передается другому элементу. Попытка прочитать переменную в процессе ввода вернет старое значение, которое было в ней до начала редактирования.
Если в конфигурации используется внешняя компонента FormEx, мы можем попытаться отловить каждое нажатие клавиши в реальном времени. Это позволяет мгновенно реагировать на ввод пользователя.
Разберем пример кода, который можно разместить в модуле формы:
Процедура ПриОтжатииКнопкиКлавиатуры(Код, Альт, Шифт, Контрол, Символ, ФСО)
// Проверяем, что фокус находится на нужном нам поле
Если Форма.АктивныйЭлемент() <> "ОписаниеТовара" Тогда
Возврат;
КонецЕсли;
// Игнорируем управляющие клавиши (курсоры, функциональные и т.д.), если нужно
// Код > 32 — это печатные символы
Если Код > 32 Тогда
флТекстИзменен = 1;
Форма.кнЗакрыть.Заголовок("&Закрыть ( ИЗМЕНЕНИЯ НЕ СОХРАНЕНЫ )");
// Чтобы заголовок кнопки обновился визуально, иногда требуется обновление формы
РасширениеФормы.Обновить();
КонецЕсли;
КонецПроцедуры
Важный нюанс: использование РасширениеФормы.Обновить() или Форма.Обновить() после каждого нажатия клавиши может привести к неприятному «морганию» экрана. Чтобы минимизировать этот эффект, рекомендуется обновлять форму только один раз при фиксации первого изменения.
Проанализируем «хитрый» штатный способ. Мы можем поместить на форму текстовый элемент (не поле ввода!), сделать его невидимым или очень маленьким и в поле «Формула» прописать вызов функции.
Эта формула вычисляется платформой довольно часто при различных интерфейсных событиях. Внутри этой функции мы можем реализовать логику сравнения. Однако, как показывает практика, для многострочных полей неограниченной длины этот метод работает нестабильно, так как значение переменной все равно остается старым до момента потери фокуса.
Это наиболее профессиональный и надежный метод. Каждое окно в Windows (включая поле ввода 1С) может отвечать на системные сообщения. Мы можем отправить сообщение WM_GETTEXT непосредственно дескриптору (hWnd) поля ввода. Это позволит получить текущий текст из «экранного» элемента, даже если 1С еще не обновила свою внутреннюю переменную.
Для реализации этого метода через FormEx или 1CPP нам потребуется:
hWnd (хендл) нужного поля ввода.SendMessage.Посмотрим на логику (псевдокод):
// Пример получения текста через WinAPI
ТекстИзПоля = "";
hWndПоля = РасширениеФормы.ПолучитьАтрибут("ОписаниеТовара").hWnd;
ТекстИзПоля = WinAPI_SendMessage_GetText(hWndПоля);
Если ТекстИзПоля <> ИсходныйТекст Тогда
флТекстИзменен = 1;
КонецЕсли;
Преимущество этого способа в том, что он позволяет отловить не только ввод с клавиатуры, но и вставку текста мышкой (через контекстное меню «Вставить»), что невозможно сделать через ПриНажатииКнопкиКлавиатуры.
Если мгновенная реакция не критична, можно запустить периодическую проверку. Рассмотрим по шагам:
ИсходныйТекст.Форма.ОбработкаОжидания("ПроверитьИзменения", 1).ПроверитьИзменения сравниваем текущее состояние.Внимание: Чтобы этот метод сработал без увода фокуса, нам снова придется использовать SendMessage, описанный в предыдущем пункте. Если просто сравнивать переменную 1С, результат будет нулевым до момента перехода на другое поле.
Если штатное поле ввода накладывает слишком много ограничений, программисты 1С 7.7 часто прибегают к использованию внешних компонент, которые ведут себя более гибко:
OnChange, которые срабатывают мгновенно.Текст на форме (в режиме редактирования) дает больше возможностей по управлению событиями, чем стандартный реквизит типа «Строка».Выясним ситуацию, когда «красивое» решение в реальном времени слишком трудозатратно. Можно реализовать проверку в момент закрытия формы или при нажатии кнопки «Закрыть».
Рассмотрим алгоритм, предложенный автором темы:
ТекстПриОткрытии.ТекстВПоле и ТекстПриОткрытии.
Процедура ПриНажатииКнопкиЗакрыть()
// Переводим фокус, чтобы обновить значение переменной ОписаниеТовара
Форма.кнЗакрыть.УстановитьФокус();
Если ОписаниеТовара <> ТекстПриОткрытии Тогда
Ответ = Вопрос("Текст был изменен. Сохранить изменения?", "Да+Нет+Отмена");
Если Ответ = "Да" Тогда
СохранитьДанные();
СтатусВозврата(1);
ИначеЕсли Ответ = "Нет" Тогда
СтатусВозврата(1);
Иначе
СтатусВозврата(0); // Остаемся в форме
КонецЕсли;
Иначе
СтатусВозврата(1);
КонецЕсли;
КонецПроцедуры
Мы проанализировали различные подходы к определению модифицированности текста. Подведем итог:
FormEx — используйте ПриОтжатииКнопкиКлавиатуры или SendMessage.SendMessage с получением текста по hWnd или использование специализированных ActiveX.Выбор метода зависит от требований к интерфейсу и готовности использовать внешние компоненты в вашей конфигурации.