Как программно вернуть курсор на нужную строку таблицы в 1С при изменении данных

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

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

В этой статье мы подробно проанализируем причины возникновения данных ошибок и рассмотрим надежные практические методы их решения как для Обычных, так и для Управляемых форм.

Выясняем причины: почему возникают ошибки позиционирования?

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

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

Вторая популярная ошибка — «Отсутствует ключ строки» — возникает при попытке полной очистки и перезаполнения источника данных таблицы. Рассмотрим типичный некорректный сценарий:


// ОШИБОЧНЫЙ МЕТОД
СтрокаНаКоторойСтоим = ЭлементыФормы.ТаблВыпуск.ТекущиеДанные; // Запоминаем строку
ТаблВыпуск.Очистить(); // Очищаем таблицу

// ... заново заполняем таблицу новыми строками ...

// Попытка вернуть курсор на сохраненный объект строки вызовет ошибку!
ЭлементыФормы.ТаблВыпуск.ТекущаяСтрока = СтрокаНаКоторойСтоим; 

При вызове метода Очистить() все физические объекты строк в оперативной памяти уничтожаются. Переменная СтрокаНаКоторойСтоим превращается в так называемую «битую ссылку» — объект, который больше не принадлежит данной таблице. Передать его свойству ТекущаяСтрока невозможно, что и приводит к ошибке отсутствия ключа.

---

Решение №1: Отказ от очистки таблицы в пользу модификации в цикле

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

Рассмотрим пример правильной реализации пересчета строк без перезаполнения таблицы:


Процедура ТаблПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
    Если ОтменаРедактирования Тогда
        Возврат;
    КонецЕсли;
    
    // Получаем текущие данные редактируемой строки
    ТекущиеДанныеРедактирования = Элемент.ТекущиеДанные;
    Если ТекущиеДанныеРедактирования = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Вместо очистки всей таблицы, обходим существующие строки
    Для Каждого СтрокаТаблицы Из ТаблВыпуск Цикл
        // Проверяем определенный критерий для изменения зависимых строк
        Если СтрокаТаблицы.ВажноеПоле = ТекущиеДанныеРедактирования.ВажноеПоле Тогда
            // Изменяем данные в ячейках напрямую
            СтрокаТаблицы.Значение = ВычислитьНовоеЗначение(СтрокаТаблицы);
        КонецЕсли;
    КонецЦикла;
    
    // Курсор автоматически останется на редактируемой строке!
КонецПроцедуры

---

Решение №2: Использование отложенного вызова (обработчик ожидания)

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

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

Разберем по шагам программный код для Обычных форм:


// В самом начале модуля формы объявляем переменную для хранения индекса строки
Перем СохраненныйИндексСтроки;

Процедура ТаблПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
    // 1. Запоминаем индекс текущей строки перед манипуляциями
    СохраненныйИндексСтроки = ТаблВыпуск.Индекс(Элемент.ТекущиеДанные);
    
    // 2. Выполняем любые операции (даже очистку и перезаполнение)
    ВыполнитьПерезаполнениеТабличнойЧасти();
    
    // 3. Подключаем однократный обработчик ожидания с минимальным интервалом 0.1 сек
    ПодключитьОбработчикОжидания("ВыполнитьПерепозиционированиеСтроки", 0.1, Истина);
КонецПроцедуры

Процедура ВыполнитьПерепозиционированиеСтроки() Экспорт
    // Проверяем, корректен ли сохраненный индекс после перезаполнения
    Если СохраненныйИндексСтроки <> Неопределено 
       И СохраненныйИндексСтроки < ТаблВыпуск.Количество() Тогда
        
        // Позиционируем курсор. Ошибки блокировки не возникнет!
        ЭлементыФормы.ТаблВыпуск.ТекущаяСтрока = ТаблВыпуск[СохраненныйИндексСтроки];
    КонецЕсли;
КонецПроцедуры

---

Решение №3: Позиционирование по уникальному бизнес-ключу

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

Проанализируем пример поиска и установки строки по ключевым реквизитам:


Перем КлючПоискаНоменклатура;

Процедура ТаблПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Запоминаем уникальный ключ строки
    КлючПоискаНоменклатура = ТекущиеДанные.Номенклатура;
    
    // Выполняем перезаполнение
    ПерезаполнитьТаблицуДанными();
    
    // Запускаем отложенный фокус
    ПодключитьОбработчикОжидания("УстановитьФокусПоКлючу", 0.1, Истина);
КонецПроцедуры

Процедура УстановитьФокусПоКлючу() Экспорт
    // Ищем строку по нашему сохраненному ключу
    ПараметрыОтбора = Новый Структура("Номенклатура", КлючПоискаНоменклатура);
    НайденныеСтроки = ТаблВыпуск.НайтиСтроки(ПараметрыОтбора);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        // Устанавливаем курсор на первую найденную строку
        ЭлементыФормы.ТаблВыпуск.ТекущаяСтрока = НайденныеСтроки[0];
    КонецЕсли;
КонецПроцедуры

---

Особенности реализации задачи на Управляемых формах (УФ)

Если вы переносите конфигурацию на Управляемый интерфейс (тонкий и веб-клиенты), вам следует учитывать принципиальное различие в работе свойства ТекущаяСтрока:

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


&НаКлиенте
Процедура УстановитьКурсорНаУправляемойФорме()
    // 1. Получаем индекс нужной строки таблицы (например, первая строка с индексом 0)
    НужныйИндекс = 0;
    
    // 2. Получаем строку коллекции данных по индексу
    СтрокаДанных = Объект.ТаблВыпуск[НужныйИндекс];
    
    // 3. Получаем уникальный системный идентификатор этой строки
    ИдентификаторСтроки = СтрокаДанных.ПолучитьИдентификатор();
    
    // 4. Устанавливаем фокус на клиенте
    Элементы.ТаблВыпуск.ТекущаяСтрока = ИдентификаторСтроки;
КонецПроцедуры

Используя приведенные методики, вы сможете гарантировать стабильную работу интерфейса без вылетов программы, сохранив при этом удобство ввода данных для конечных пользователей системы.

← На главную