При разработке интерфейсов на платформе 1С 7.7 с использованием расширения 1С++ часто возникает задача динамического отображения хода выполнения процесса. Типичный пример: выполнение длительного цикла обработки объектов, результаты которого должны сразу появляться в ТабличномПоле (ТП), создавая эффект "живого лога". Однако программисты часто сталкиваются с тем, что при добавлении каждой новой строки интерфейс начинает "дергаться" или мерцать, что мешает комфортной работе пользователя. Рассмотрим подробнее, как решить эту задачу эффективно и профессионально.
Для начала проанализируем, почему возникает мерцание. В связке ТабличноеПоле + ПоставщикДанныхИТ + ИндексированнаяТаблица (ИТ) данные хранятся в объекте ИТ, а провайдер (ПоставщикДанных) выступает мостом между таблицей в памяти и визуальным элементом управления. Когда мы добавляем строку в ИТДанные и вызываем методы обновления ТП, элемент управления Windows перерисовывает свои границы и содержимое.
В компоненте 1С++ существуют два основных метода для обновления визуального состояния:
ОбновитьСтроки() (RefreshRows) — этот метод заставляет ТП полностью сбросить внутренний кэш и перечитать данные из провайдера. Это "тяжелая" операция, вызывающая полную перерисовку.ПерерисоватьСтроки() (RedrawRows) — более легкий метод. Согласно документации, он обновляет строки без обращения к провайдеру, инициируя стандартное событие перерисовки окна (Windows Invalidate).Посмотрим на классический пример кода, который часто приводит к мерцанию:
// В цикле обработки
ИТДанные.НоваяСтрока();
ИТДанные.ТекстСообщения = "Обработка объекта " + ТекущийОбъект;
мТабличноеПоле.ТекущаяСтрока = ИТДанные.КоличествоСтрок();
мТабличноеПоле.ОбновитьСтроки();
Выясним причину, по которой интерфейс "прыгает". Экспериментальным путем установлено, что основное влияние на визуальную стабильность оказывает полоса прокрутки (вертикальный скроллер). Если свойство ВертСкроллер установлено в значение 1 (всегда включен), то при добавлении каждой строки Windows пересчитывает размер "бегунка" и положение всей клиентской области окна.
Разберем ситуацию: мерцание наиболее заметно, пока таблица не заполнена полностью (то есть количество строк меньше высоты видимой части ТП). Как только данных становится много и появляется необходимость прокрутки, поведение меняется.
Рекомендация: попробуем использовать автоматический режим скроллера. Вместо жесткой установки мТабличноеПоле.ВертСкроллер = 1;, лучше оставить его в авторежиме или управлять им динамически. Это значительно снижает количество лишних перерисовок рамки окна.
Рассмотрим ситуацию с точки зрения производительности. Если цикл выполняется очень быстро (сотни итераций в секунду), вызывать ОбновитьСтроки() на каждом шаге — избыточно. Человеческий глаз не способен воспринять изменения с такой скоростью, а ресурсы процессора будут тратиться на отрисовку UI.
Проанализируем стратегию "пакетного обновления":
ИндексированнаяТаблица.ПерерисоватьСтроки() вместо ОбновитьСтроки(), если структура таблицы (состав колонок) не менялась.Часто пользователю нужно видеть именно последнюю добавленную строку ("автоскролл"). Для этого используется свойство ТекущаяСтрока. Однако здесь есть важный нюанс, связанный с индексами. В ПоставщикеДанныхИТ визуальный индекс строки в ТП может не совпадать с физическим номером строки в ИТ (например, если включена сортировка или фильтрация).
Если мы хотим гарантированно перейти в конец списка, нужно учитывать, что ТекущаяСтрока ожидает номер строки в контексте текущего представления провайдера. Посмотрим на правильный подход:
// После добавления данных
мТабличноеПоле.ТекущаяСтрока = мПоставщикТабличногоПоля.КоличествоСтрок();
мТабличноеПоле.ПерерисоватьСтроки();
Если стандартное присвоение ТекущаяСтрока = 1 не прокручивает таблицу вверх (например, застряв на 18-й строке), это может означать, что фокус установлен, но команда "показать строку" (EnsureVisible) не отработала. В таких случаях помогает связка установки текущей строки и принудительного обновления.
Для продвинутых разработчиков существует метод полной блокировки отрисовки окна на время массовых изменений. Это делается через отправку сообщения WM_SETREDRAW окну Табличного Поля. Хотя 1С++ старается делать это внутренними средствами, при очень интенсивных обновлениях ручное управление может помочь.
Разберем алгоритм:
SendMessage(hWnd, WM_SETREDRAW, 0, 0).SendMessage(hWnd, WM_SETREDRAW, 1, 0).InvalidateRect для принудительного обновления всей области.Однако в большинстве случаев для 1С 7.7 достаточно просто заменить ОбновитьСтроки() на ПерерисоватьСтроки() и грамотно настроить ВертСкроллер.
Подводя итог, для реализации плавного и красивого обновления таблицы в 1С++ следуйте этим правилам:
ТекущаяСтрока, обращаясь к количеству строк именно в объекте-провайдере, а не в исходной таблице значений, чтобы избежать ошибок индексации.Соблюдение этих простых рекомендаций позволит сделать ваши обработки профессиональными, а работу пользователя — комфортной.