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