Как реализовать «живой» поиск при вводе символов в 1С 7.7

Программист 1С v7.7
← На главную

В современных интерфейсах пользователи привыкли к «живому» поиску: когда результаты в списке фильтруются мгновенно по мере набора текста, без необходимости нажимать Enter или специальные кнопки. В платформе 1С 7.7 реализация такого механизма на штатных элементах управления (полях ввода) затруднена, так как значение связанной переменной обновляется только после потери фокуса или завершения ввода. Рассмотрим подробно, как обойти эти ограничения, используя возможности внешних компонент FormEx, 1CPP и 1sqlite.

Проблема штатных полей ввода и «эффект задвоения»

Разберем ситуацию: если мы пытаемся использовать процедуру ПриНажатииКнопкиКлавиатуры из библиотеки FormEx для перехвата каждого символа, мы сталкиваемся с тем, что ввод в штатное текстовое поле начинает «глючить». При быстром наборе символы могут задваиваться или пропускаться. Это происходит из-за того, что программное изменение значения атрибута формы во время события нажатия клавиши конфликтует с внутренним буфером платформы 1С.

Проанализируем типичный ошибочный код, который приводит к нестабильной работе:


Процедура ПриНажатииКнопкиКлавиатуры(КодКлавиши, Альт, Шифт, Контрол, Символ, ФлСт)
    Если Форма.АктивныйЭлемент() = "СтрПоиска" Тогда
        // Программная достройка строки
        ПодстрокаПоиска = ПодстрокаПоиска + Символ;
        СтрПоиска = ПодстрокаПоиска; // Здесь происходит конфликт с буфером ввода
        ПопробоватьНайти();
    КонецЕсли;
КонецПроцедуры

Выясним причину: когда мы присваиваем значение СтрПоиска = ПодстрокаПоиска, поле перерисовывается, и курсор может сброситься, а операционная система может повторно послать сообщение о нажатой клавише. Чтобы решить эту проблему качественно, нам нужно либо использовать таймеры для задержки поиска, либо полностью сменить элемент управления на ActiveX.

Решение 1: Использование таймера (Debouncing)

Если мы хотим оставить штатное поле ввода, нам необходимо реализовать «защиту от дребезга» (debouncing). Рассмотрим, как это сделать. Вместо того чтобы запускать тяжелый SQL-запрос на каждое нажатие, мы будем взводить таймер. Если пользователь нажал следующую клавишу быстрее чем через 300 мс, предыдущий вызов поиска отменяется.

Для этого воспользуемся методом ОбработкаОжидания из FormEx:


Процедура ПриНажатииКнопкиКлавиатуры(КодКлавиши, Альт, Шифт, Контрол, Символ, ФлСт)
    // Просто фиксируем факт нажатия, поиск не запускаем
    Расш.ОбработкаОжидания("ЗапуститьПоиск", 300); 
КонецПроцедуры

Процедура ЗапуститьПоиск()
    Расш.ОбработкаОжидания("ЗапуститьПоиск", 0); // Останавливаем таймер
    // Выполняем поиск через SQLite для максимальной скорости
    НайтиТоварSqlLite(СокрЛП(СтрПоиска));
КонецПроцедуры

Этот метод помогает убрать «фризы» интерфейса, но не решает проблему позиционирования курсора внутри строки поиска. Поэтому перейдем к более профессиональному решению.

Решение 2: Использование ActiveX-контрола (TextBox)

Наиболее стабильный способ реализовать живой поиск — внедрить на форму сторонний элемент управления TextBox (из состава Microsoft Forms) или RichTextBox. В отличие от родных полей 1С, ActiveX-объекты позволяют мгновенно получать текст через событие Change и корректно управлять позицией курсора (SelStart).

Разберем по шагам, как внедрить такой контрол с помощью 1CPP:

  1. Разместите на форме обычную кнопку и назовите ее, например, КонтейнерПоиска.
  2. В процедуре ПослеОткрытия создайте на месте этой кнопки объект ActiveX.

Перем КонтролТекста;

Процедура ПослеОткрытия()
    АтрФормы = СоздатьОбъект("АктивИкс");
    АтрФормы.УстановитьАтрибут(Форма, "КонтейнерПоиска");
    Попытка
        // Пытаемся создать стандартный TextBox (требует MS Office)
        КонтролТекста = АтрФормы.СоздатьЭУ("Forms.TextBox.1");
    Исключение
        // Если Office не установлен, используем альтернативу (например, RichTextBox)
        КонтролТекста = АтрФормы.СоздатьЭУ("VBCCR16.RichTextBox");
    КонецПопытки;
КонецПроцедуры

Теперь мы можем обрабатывать события этого контрола напрямую в модуле формы. Нам больше не нужно перехватывать глобальные нажатия клавиш — достаточно события Change (изменение текста).


// Имя процедуры строится как ИдентификаторКонтейнера + "_" + ИмяСобытия
Процедура КонтейнерПоиска_Change()
    ТекстДляПоиска = КонтролТекста.Text;
    Если СтрДлина(СокрЛП(ТекстДляПоиска)) >= 3 Тогда
        ВыполнитьБыстрыйПоиск(ТекстДляПоиска);
    КонецЕсли;
КонецПроцедуры

Оптимизация поиска: Использование 1sqlite

Чтобы поиск действительно был «живым», он должен отрабатывать быстрее 100 мс. Обычные запросы к DBF-файлам через Справочник.ВыбратьЭлементы не подойдут для больших баз. Рассмотрим, как применить 1sqlite для мгновенной выборки.

Проанализируем пример функции поиска по нескольким фрагментам слов:


Функция НайтиТоварSqlLite(Наим)
    БазаДанных = СоздатьОбъект("SQLiteBase");
    БазаДанных.Открыть(":memory:"); // Открываем базу в памяти для скорости
    Запрос = БазаДанных.НовыйЗапрос();
    
    // Подключаем таблицу номенклатуры
    Запрос.ВыполнитьЗапрос("create virtual table Товары using dbeng(Справочник.Номенклатура)");
    
    // Разбиваем строку поиска на отдельные слова для поиска через LIKE
    ТекстЗапроса = "SELECT code Код, id [Товар :Справочник.Номенклатура] FROM Товары WHERE isfolder=2";
    
    Для Индекс = 1 По СтрКоличествоСтрок(СтрЗаменить(Наим, " ", РазделительСтрок)) Цикл
        Слово = СтрПолучитьСтроку(СтрЗаменить(Наим, " ", РазделительСтрок), Индекс);
        ТекстЗапроса = ТекстЗапроса + " AND descr LIKE '%" + Слово + "%'";
    КонецЦикла;
    
    ТзРезультат = Запрос.ВыполнитьЗапрос(ТекстЗапроса);
    // Выгружаем ТзРезультат в таблицу на форме
КонецФункции

Важный момент: Использование :memory: базы и виртуальных таблиц позволяет искать по справочнику в десятки тысяч позиций практически мгновенно.

Тонкости работы с модальными окнами и очистка ресурсов

При использовании ActiveX в 1С 7.7 важно учитывать специфику модальных окон. В некоторых случаях события KeyPress могут не доходить до контрола, если форма открыта модально. В такой ситуации рекомендуется использовать именно событие _Change, которое срабатывает на уровне Windows-сообщений самого контрола.

Также не забывайте о корректном завершении работы. Чтобы избежать «падения» 1С при закрытии формы, необходимо явно уничтожать объект ActiveX:


Процедура ПриЗакрытии()
    // Принудительная очистка ссылок для предотвращения Crash при выходе
    АтрФормы.Destroy(); 
    КонтролТекста = 0;
КонецПроцедуры

Дополнительные возможности: Копирование и Вставка

Чтобы сделать поле поиска полноценным, добавим поддержку горячих клавиш Ctrl+C и Ctrl+V. В ActiveX-контролах это реализуется через событие KeyUp:


Процедура КонтейнерПоиска_KeyUp(Клавиша, Шифт)
    // Шифт = 2 соответствует нажатому Ctrl
    Если (Клавиша.value = 67) И (Шифт = 2) Тогда // Ctrl+C
        КонтролТекста.Copy();
    ИначеЕсли (Клавиша.value = 86) И (Шифт = 2) Тогда // Ctrl+V
        КонтролТекста.Paste();
    КонецЕсли;
КонецПроцедуры

Резюме: Мы проанализировали путь от простого перехвата клавиш до полноценного использования ActiveX и SQLite. Рассмотренный метод с использованием ActiveX TextBox является наиболее надежным, так как он полностью передает управление вводом специализированному объекту, оставляя за 1С только задачу обработки полученного текста и вывода результатов поиска.

← На главную