При разработке интерфейсов на управляемых формах 1С часто возникает типовая задача: пользователь редактирует значение в ячейке дерева, после чего система должна выполнить пересчет зависимых данных и вернуть курсор точно в ту же ячейку, где находился пользователь. Однако на практике разработчики часто сталкиваются с ситуацией, когда курсор сбрасывается в начало таблицы, фокус уходит на первый элемент формы, либо строка подсвечивается как выбранная, но фокус клавиатуры в ней отсутствует.
Разберем по шагам, почему возникает такое поведение платформы и как правильно организовать позиционирование на строке и конкретной ячейке дерева значений.
Проанализируем основные факторы, приводящие к потере текущей строки и ячейки:
ЗначениеВРеквизитФормы(). Если на сервере для пересчета коллекция дерева выгружается в обычное ДеревоЗначений, пересчитывается, а затем загружается обратно методом ЗначениеВРеквизитФормы(), платформа полностью пересоздает объект ДанныеФормыДерево. При этом внутренние числовые идентификаторы строк генерируются заново. Идентификатор, полученный на клиенте до серверного вызова, становится недействительным.ТекущаяСтрока отвечает только за позиционирование в источнике данных. Присвоение ему значения действительно делает строку активной (она может выделяться цветом), но не активизирует саму колонку и не переводит фокус ввода в элемент интерфейса.ТекущийЭлемент.Рассмотрим наиболее правильный с точки зрения архитектуры управляемых форм подход. Чтобы идентификаторы строк не сбрасывались, а состояние раскрытых узлов сохранялось, откажемся от конвертации через ЗначениеВРеквизитФормы() и выполним пересчет прямо в объекте ДанныеФормыДерево.
Посмотрим на пример обработки события при изменении ячейки:
&НаКлиенте
Процедура ДеревоКолонкаСуммаПриИзменении(Элемент)
// Запоминаем текущую строку и текущую колонку
ТекущийИдентификатор = Элементы.Дерево.ТекущаяСтрока;
ТекущаяКолонка = Элементы.Дерево.ТекущаяКолонка;
// Выполняем пересчет на сервере без перезаполнения дерева целиком
ПересчитатьДанныеНаСервере(ТекущийИдентификатор);
// Восстанавливаем позиционирование
Элементы.Дерево.ТекущаяСтрока = ТекущийИдентификатор;
Элементы.Дерево.ТекущаяКолонка = ТекущаяКолонка;
// Возвращаем фокус ввода в нужную колонку дерева
ЭтаФорма.ТекущийЭлемент = ТекущаяКолонка;
КонецПроцедуры
&НаСервере
Процедура ПересчитатьДанныеНаСервере(ИдентификаторСтроки)
// Получаем строку дерева по ее неизменному идентификатору
СтрокаДерева = Дерево.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаДерева <> Неопределено Тогда
// Выполняем расчет зависимых показателей непосредственно в коллекции формы
СтрокаДерева.Итог = СтрокаДерева.Количество * СтрокаДерева.Цена;
КонецЕсли;
КонецПроцедуры
В этом примере структура данных не пересоздается, числовые идентификаторы узлов остаются стабильными, а передача ссылки на колонку в ЭтаФорма.ТекущийЭлемент гарантированно возвращает курсор в редактируемую ячейку.
Проанализируем ситуацию, когда форму вообще не требуется отправлять на сервер. Если алгоритм пересчета сводится к базовым математическим операциям над ячейками строки или родительских узлов, вызов сервера является избыточным.
Выполняя расчет прямо на клиенте, мы полностью исключаем задержки сетевого взаимодействия и риск потери фокуса ввода:
&НаКлиенте
Процедура ДеревоКоличествоПриИзменении(Элемент)
ТекущиеДанные = Элементы.Дерево.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
ТекущиеДанные.Сумма = ТекущиеДанные.Количество * ТекущиеДанные.Цена;
КонецЕсли;
// Фокус остается в текущей ячейке автоматически без вызовов сервера
КонецПроцедуры
Рассмотрим случай, когда пересчет затрагивает сложную серверную бизнес-логику с запросами к базе данных и переформированием структуры дерева через выгрузку в ДеревоЗначений. В этом сценарии идентификаторы платформы будут пересозданы, поэтому позицию необходимо восстанавливать по уникальному пользовательскому ключу.
КлючСтроки (строковый GUID или уникальный код).
ТекущиеДанные = Элементы.Дерево.ТекущиеДанные;
КлючАктивнойСтроки = ТекущиеДанные.КлючСтроки;
КолонкаДляФокуса = Элементы.Дерево.ТекущаяКолонка;
ЗначениеВРеквизитФормы() выполним поиск строки по сохраненному ключу, обратившись к элементам дерева через метод ПолучитьЭлементы().ПолучитьИдентификатор() и передадим новое значение в Элементы.Дерево.ТекущаяСтрока.Элементы.Дерево.ТекущаяКолонка = КолонкаДляФокуса; и ЭтаФорма.ТекущийЭлемент = КолонкаДляФокуса;.Если после возврата курсора в ячейку требуется сразу перевести ее в режим ввода текста (без необходимости повторного двойного клика мыши или нажатия клавиши Enter), применим метод таблицы ИзменитьСтроку():
Элементы.Дерево.ТекущаяСтрока = ТекущийИдентификатор;
Элементы.Дерево.ТекущаяКолонка = Элементы.ДеревоКолонкаСумма;
ЭтаФорма.ТекущийЭлемент = Элементы.Дерево;
Элементы.Дерево.ИзменитьСтроку();
Если ветка дерева с активной строкой могла свернуться при обновлении, перед установкой текущей строки развернем родительский узел с помощью метода Элементы.Дерево.Развернуть(ИдентификаторРодителя, Истина);. В таком комплексе настроек интерфейс сохраняет полную стабильность при любых серверных пересчетах.