При разработке управляемых форм в 1С:Предприятии 8.3 программисты часто сталкиваются с неприятным эффектом: после выполнения расчетов в таблице значений фокус с активной ячейки пропадает, а выделение строки сбрасывается. Это происходит из-за особенностей работы платформы с данными формы, особенно когда используются методы преобразования коллекций. В данной статье мы подробно разберем, почему это происходит, как оптимизировать код для сохранения стабильности интерфейса и как реализовать удобный переход на следующую строку после ввода данных.
Разберем первопричину проблемы. Когда в коде вызывается метод ЗначениеВРеквизитФормы, платформа 1С полностью обновляет содержимое объекта на форме. С точки зрения графического интерфейса это выглядит как удаление всей старой таблицы и создание на её месте новой с теми же данными. В этот момент все временные состояния элемента формы, такие как активная строка и фокус ввода, аннулируются, так как старые идентификаторы строк перестают существовать. Даже если данные выглядят идентично, для платформы это новые объекты.
Проанализируем ситуацию автора темы: для расчета итогов по колонкам он переводит таблицу в объект ТаблицаЗначений через РеквизитФормыВЗначение, выполняет циклы и возвращает её обратно. Именно этот «прыжок» между типами данных приводит к потере фокуса.
Самый эффективный способ решить проблему потери фокуса — это вообще отказаться от использования методов РеквизитФормыВЗначение и ЗначениеВРеквизитФормы там, где это возможно. В управляемых формах реквизиты типа «Таблица значений» представлены объектом ДанныеФормыКоллекция. С ним можно работать почти так же, как с обычной таблицей значений: перебирать строки, обращаться к колонкам по имени и изменять значения.
Рассмотрим, как можно переписать расчет итогов, сохранив фокус в ячейке. Вместо выгрузки всей таблицы, мы будем работать напрямую с реквизитом ТЗ (который является частью Объект или просто реквизитом формы). Посмотрим на пример кода:
// Определяем имена колонок динамически
ИмяОрганизации = УдалитьЛишниеСимволыИзНаименования(Объект.Организация);
ИмяКолонкиОСТАТОК = "ОСТАТОК" + ИмяОрганизации;
ИмяКолонкиИТОГ = "ИТОГ" + ИмяОрганизации;
// Собираем массив колонок, по которым нужно считать сумму (склады)
МассивКолонокСоСкладами = Новый Массив;
Для Каждого ТекКолонка Из Элементы.ТЗ.ПодчиненныеЭлементы Цикл
Если СтрНайти(ТекКолонка.Имя, "СКЛАД_") <> 0 Тогда
МассивКолонокСоСкладами.Добавить(ТекКолонка.Имя);
КонецЕсли;
КонецЦикла;
// Работаем напрямую с ДанныеФормыКоллекция (реквизит ТЗ на форме)
Для Каждого Стр Из ТЗ Цикл
// Инициализируем расчетные значения
Стр[ИмяКолонкиОСТАТОК] = Стр[ИмяОрганизации];
Стр[ИмяКолонкиИТОГ] = 0;
// Суммируем данные по складам
Для Каждого ИмяСклада Из МассивКолонокСоСкладами Цикл
Стр[ИмяКолонкиИТОГ] = Стр[ИмяКолонкиИТОГ] + Стр[ИмяСклада];
Стр[ИмяКолонкиОСТАТОК] = Стр[ИмяКолонкиОСТАТОК] - Стр[ИмяСклада];
КонецЦикла;
КонецЦикла;
Преимущество этого подхода: данные меняются «на лету» непосредственно в структуре формы. Платформа не перерисовывает таблицу целиком, а лишь обновляет значения в ячейках. Фокус ввода и текущая строка при этом остаются на своих местах.
Если специфика задачи все же требует использования ЗначениеВРеквизитФормы (например, при вызове сложных серверных процедур, работающих только с типом ТаблицаЗначений), нам необходимо научиться восстанавливать состояние таблицы вручную. Для этого используется идентификатор строки.
Выясним важный нюанс: свойство ТекущаяСтрока у элемента формы содержит не индекс и не саму строку, а внутренний числовой идентификатор. Разберем по шагам алгоритм восстановления:
ЗначениеВРеквизитФормы) принудительно устанавливаем сохраненный идентификатор обратно в свойство ТекущаяСтрока.Пример реализации:
// 1. Запоминаем текущую позицию
СохраненныйИдентификатор = Элементы.ТЗ.ТекущаяСтрока;
// 2. Выполняем действия с преобразованием данных
ТЗОбъект = РеквизитФормыВЗначение("ТЗ");
// ... какие-то расчеты в ТЗОбъект ...
ЗначениеВРеквизитФормы(ТЗОбъект, "ТЗ");
// 3. Восстанавливаем фокус
// Важно: идентификаторы строк могут измениться, если таблица была полностью пересоздана.
// Если строки те же, просто присваиваем:
Элементы.ТЗ.ТекущаяСтрока = СохраненныйИдентификатор;
Нужно учитывать, что если при обновлении данных строки удалялись или сортировались, старый идентификатор может стать недействительным. В этом случае вместо сохранения идентификатора нужно найти строку заново по ключевым полям (например, по Артикулу или Коду Номенклатуры), используя метод НайтиСтроки() у реквизита формы, и получить новый идентификатор методом ПолучитьИдентификатор().
Часто бизнес-процесс требует, чтобы после ввода числа в ячейку курсор автоматически прыгал на строку ниже (имитация поведения Excel). Для этого проанализируем совместное использование индекса строки и свойств активизации.
Рассмотрим алгоритм перехода:
&НаКлиенте
Процедура ТЗПриИзменении(Элемент)
// Получаем текущие данные
ТекущиеДанные = Элементы.ТЗ.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Находим индекс текущей строки в коллекции
ТекущийИндекс = ТЗ.Индекс(ТекущиеДанные);
// Проверяем, есть ли строка ниже
Если ТекущийИндекс < ТЗ.Количество() - 1 Тогда
// Получаем следующую строку
СледующаяСтрока = ТЗ[ТекущийИндекс + 1];
// Устанавливаем активность на следующую строку по её идентификатору
Элементы.ТЗ.ТекущаяСтрока = СледующаяСтрока.ПолучитьИдентификатор();
// Если нужно активизировать конкретную колонку (например, ту же самую)
// Элементы.ТЗ.ТекущийЭлемент = Элементы.ТЗКоличество;
КонецЕсли;
КонецПроцедуры
Важный момент: Чтобы пользователь мог сразу продолжить ввод в новой строке без лишнего клика мышкой, необходимо убедиться, что фокус находится на самом элементе таблицы. Используйте конструкцию Элементы.ТЗ.ТекущийЭлемент = Элементы.ТЗ; для принудительного возврата фокуса в таблицу.
Проанализировав проблему, мы можем сделать следующие выводы:
РеквизитФормыВЗначение в обработчиках событий ячеек (ПриИзменении, ОкончаниеВводаТекста). Это «тяжелая» операция, которая не только сбивает фокус, но и нагружает сервер и сеть.ДанныеФормыКоллекция. Это самый надежный способ сохранить пользовательский контекст (позицию курсора, выделение текста).ТаблицаЗначений, всегда восстанавливайте ТекущаяСтрока, используя методы ПолучитьИдентификатор() или поиск по уникальному ключу строки.Индекс() -> ПолучитьИдентификатор() -> ТекущаяСтрока.Соблюдение этих правил позволит создать интерфейс, который будет работать плавно и предсказуемо для пользователя, избавляя его от необходимости постоянно возвращать курсор в нужное место вручную.