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

Программист 1С v8.3 (Обычные формы)
← На главную

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

Часть 1. Запрет обновления и устранение мерцания в обычных формах

В «Обычных формах» 1С (в отличие от «Управляемых») отсутствует встроенный метод ЭтаФорма.Обновить() с параметром запрета перерисовки. Когда мы в цикле меняем свойства Видимость, Доступность или заголовки у десятков элементов, операционная система пытается перерисовать форму после каждого изменения. Это и создает эффект «дребезга» интерфейса. Разберем основные способы решения этой проблемы.

Метод 1. Использование группировки и управления видимостью

Самый простой и эффективный способ в рамках штатных средств — это объединение всех динамически изменяемых элементов в одну группу (рамку группы) или на одну панель. Проанализируем алгоритм действий:

  1. Разместим все элементы, которые подлежат массовому обновлению, внутри одного контейнера (например, Панель или Группа).
  2. Перед началом расчетов и массового изменения свойств установим ЭлементыФормы.ИмяГруппы.Видимость = Ложь;.
  3. Выполним все необходимые манипуляции с вложенными элементами.
  4. В самом конце вернем ЭлементыФормы.ИмяГруппы.Видимость = Истина;.

Этот подход заставляет платформу перерисовать всё содержимое контейнера один раз, а не для каждого элемента по отдельности. Рассмотрим пример кода:


// Скрываем контейнер для оптимизации перерисовки
ЭлементыФормы.ОсновнаяПанель.Видимость = Ложь;

Для Каждого Элемент Из СписокЭлементов Цикл
    // Выполняем множество изменений
    Элемент.Доступность = ПроверитьДоступность(Элемент);
    Элемент.ЦветТекста = ОпределитьЦвет(Элемент);
КонецЦикла;

// Возвращаем видимость одним действием
ЭлементыФормы.ОсновнаяПанель.Видимость = Истина;

Метод 2. Использование многостраничных панелей

Если скрытие основной области формы выглядит «некрасиво», мы можем применить архитектурный прием с переключением страниц. Разберем этот вариант по шагам:

  1. Создадим на форме объект Панель без отображения закладок.
  2. На первой странице разместим рабочие элементы.
  3. На второй странице разместим надпись «Пожалуйста, подождите...» и, возможно, индикатор процесса.
  4. Перед началом обработки программно переключим панель на вторую страницу.
  5. После завершения всех расчетов и настроек элементов на первой странице — вернемся на неё.

Это не только убирает мерцание, но и дает пользователю понять, что система занята выполнением задачи.

Метод 3. Низкоуровневая «заморозка» формы через WinAPI (Для экспертов)

Для тех случаев, когда штатные методы не помогают, можно прибегнуть к использованию функций операционной системы Windows. Мы можем отправить окну формы сообщение WM_SETREDRAW, которое полностью запрещает перерисовку до тех пор, пока мы не разрешим её снова. Выясним, как это реализовать:


// Определение внешней функции
#Если Клиент Тогда
    Процедура ЗаблокироватьПерерисовку(Знач ХэндлОкна, Знач Состояние) Экспорт
        // Подключаем user32.dll для работы с сообщениями Windows
        Попытка
            User32 = Новый COMОбъект("DynamicWrapperX"); // Или использование внешних компонент
            User32.Register("user32.dll", "SendMessageA", "i=llll", "f=s", "r=l");
            User32.SendMessageA(ХэндлОкна, 11, Состояние, 0); // 11 - это WM_SETREDRAW
        Исключение
            // Обработка ошибки, если компонента не зарегистрирована
        КонецПопытки;
    КонецПроцедуры
#КонецЕсли

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

Часть 2. Расчет рейтинга (места) с учетом одинаковых значений

Перейдем ко второму вопросу: как правильно пронумеровать строки в отчете, если у нескольких объектов (например, сотрудников или товаров) одинаковые числовые показатели, и им нужно присвоить одно и то же место? Это называется «плотным рейтингом» (Dense Rank).

Метод 1. Соединение таблицы самой с собой в запросе

Проанализируем классический подход через язык запросов 1С. Основная идея заключается в том, чтобы для каждой записи подсчитать количество уникальных значений, которые больше (или меньше) текущего значения.

Предположим, у нас есть таблица с полями Сотрудник и Показатель. Нам нужно вывести место:


ВЫБРАТЬ
    Т1.Сотрудник,
    Т1.Показатель,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т2.Показатель) КАК Место
ИЗ
    ВТ_Данные КАК Т1
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
        ПО Т1.Показатель < Т2.Показатель
СГРУППИРОВАТЬ ПО
    Т1.Сотрудник,
    Т1.Показатель
УПОРЯДОЧИТЬ ПО
    Т1.Показатель УБЫВ

Разберем, как это работает: если у нас есть показатели 100, 90, 90, 80, то для значения 90 условие Т1.Показатель < Т2.Показатель сработает только для значения 100. Количество различных значений будет равно 1, и прибавив 1 (или используя <=), мы получим второе место для обоих сотрудников с показателем 90.

Метод 2. Оптимизация для больших объемов данных

Как справедливо отмечено в обсуждении, прямое соединение таблицы на 500 записей самой с собой порождает 250 000 временных строк в памяти СУБД. Если данных много (тысячи записей), используем двухэтапный расчет:

  1. Сначала выберем РАЗЛИЧНЫЕ показатели во временную таблицу ВТ_УникальныеПоказатели.
  2. Пронумеруем только эти уникальные показатели, соединив их самих с собой. Уникальных значений всегда значительно меньше, чем общих записей.
  3. Соединим исходную таблицу данных с пронумерованной таблицей показателей по полю Показатель.

Этот способ позволяет избежать геометрической прогрессии нагрузки на сервер.

Метод 3. Использование оконных функций (Для платформы 8.3.20+)

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


ВЫБРАТЬ
    Сотрудник,
    Показатель,
    ПЛОТНЫЙ_НОМЕР() ПО (УПОРЯДОЧИТЬ ПО Показатель УБЫВ) КАК Место
ИЗ
    Справочник.Сотрудники

Функция ПЛОТНЫЙ_НОМЕР() (аналог DENSE_RANK в SQL) автоматически присваивает одинаковые номера одинаковым значениям, при этом следующему за ними значению присваивается номер, идущий непосредственно по порядку (без «дырок»).

Метод 4. Расчет при обходе результата запроса (Линейная сложность)

Иногда проще и быстрее рассчитать места не в самом запросе, а при выводе данных в коллекцию или таблицу. Посмотрим на алгоритм:


Результат = Запрос.Выполнить().Выбрать();
ТекущееМесто = 0;
ПредыдущееЗначение = Неопределено;

Пока Результат.Следующий() Цикл
    Если Результат.Показатель <> ПредыдущееЗначение Тогда
        ТекущееМесто = ТекущееМесто + 1;
        ПредыдущееЗначение = Результат.Показатель;
    КонецЕсли;
    
    // Используем ТекущееМесто для вывода
    Сообщить(Результат.Сотрудник + ": " + ТекущееМесто + " место");
КонецЦикла;

Данный метод работает за один проход по данным ($O(N)$), что делает его самым производительным для огромных выборок, если не требуется сложная сортировка внутри самого SQL-сервера.

Подведем итог: для предотвращения мерцания в обычных формах используйте временное скрытие групп элементов или переключение страниц панели. Для реализации системы рейтингов выбирайте между оконными функциями (в новых версиях 1С) или алгоритмом сравнения с предыдущим значением при обходе выборки.

← На главную