В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда на управляемой форме при переключении между закладками (страницами) данные в таблицах значений ведут себя нестабильно. Текущая строка сбрасывается на самую первую, а установленные пользователем отборы могут исчезать. Особенно остро эта проблема проявляется в режиме толстого клиента (управляемое приложение), хотя сама форма может отлично работать в обычном приложении.
В этой статье мы подробно разберем, почему платформа ведет себя таким образом, проанализируем внутренние механизмы синхронизации данных между клиентом и сервером и найдем эффективные способы сохранения состояния интерфейса.
Первое, что нам необходимо понять: в управляемом приложении (даже если используется толстый клиент) работа с формой коренным образом отличается от обычного приложения. В обычном приложении элементы управления напрямую связаны с объектами в памяти. В управляемых формах данные существуют в виде структуры ДанныеФормы, которая постоянно синхронизируется с сервером.
Проанализируем основные причины сброса выделения:
ПриСменеСтраницы не назначен обработчик, при переключении закладок могут срабатывать другие события, такие как ПриАктивизацииЭлемента или ПриАктивизацииОбласти. Если в этих процедурах вызывается серверный метод с директивой &НаСервере, платформа выполняет полную синхронизацию контекста формы.РеквизитФормыВЗначение и последующая обратная конвертация ЗначениеВДанныеФормы, платформа полностью пересоздает коллекцию строк таблицы. С точки зрения системы это абсолютно новая таблица, и старые идентификаторы строк (ID) становятся невалидными. Курсор закономерно прыгает на первую строку.Рассмотрим наш код на наличие лишних обращений к серверу. Нам нужно минимизировать вызовы с передачей контекста всей формы. Посмотрим на пример кода, который может вызывать проблему:
&НаКлиенте
Процедура ПриАктивизацииЭлемента(Элемент)
// Если этот код срабатывает при переключении страницы,
// происходит передача всей формы на сервер
ОбработатьДанныеНаСервере();
КонецПроцедуры
&НаСервере
Процедура ОбработатьДанныеНаСервере()
// Любые манипуляции здесь могут привести к обновлению ТЗ
КонецПроцедуры
Чтобы избежать этой проблемы, проанализируем ситуацию: действительно ли нам нужен контекст формы? Если нет, используйте директиву &НаСервереБезКонтекста. Это исключит передачу данных формы на сервер и обратно, сохранив состояние элементов управления нетронутым.
Если серверные вызовы неизбежны, мы можем применить механизм принудительного сохранения и восстановления позиции. Для этого создадим переменную в модуле формы или специальный реквизит для хранения идентификатора строки.
Разберем по шагам, как это реализовать:
ТекущийИдентификаторСтроки (тип: Число).Пример реализации:
&НаКлиенте
Процедура СохранитьИВосстановитьПозицию()
// Сохраняем идентификатор текущей строки таблицы "Товары"
Идентификатор = Элементы.Товары.ТекущаяСтрока;
// Выполняем серверное действие
ОбновитьДанныеНаСервере();
// Восстанавливаем позицию
Элементы.Товары.ТекущаяСтрока = Идентификатор;
КонецПроцедуры
Важный нюанс: Этот метод сработает только в том случае, если идентификаторы строк не изменились (например, не было полной перезагрузки таблицы из объекта ТаблицаЗначений).
Если ваша логика требует преобразования таблицы в объект типа ТаблицаЗначений для использования методов НайтиСтроки или Свернуть, помните, что обратная загрузка ЗначениеВДанныеФормы — это "тяжелая" операция для интерфейса.
Рассмотрим, как правильно выполнять такие преобразования, чтобы не "пугать" пользователя прыгающим курсором:
&НаСервере
Процедура ОбновитьТаблицуБезПотериПозиции()
// Переводим данные формы в прикладной объект
ТЗ = РеквизитФормыВЗначение("Объект.Товары");
// Выполняем действия, например, сортировку
ТЗ.Сортировать("Номенклатура Возр");
// Загружаем обратно
ЗначениеВДанныеФормы(ТЗ, Объект.Товары);
// Платформа обновила ID строк, здесь может потребоваться
// дополнительный поиск соответствия старых строк новым
КонецПроцедуры
Чтобы избежать полной потери фокуса при использовании ЗначениеВДанныеФормы, старайтесь обновлять только те колонки, которые изменились, обращаясь к элементам коллекции напрямую через Объект.Товары[Индекс], не конвертируя всю таблицу целиком.
Хотя автор вопроса указал, что обработчик отсутствует, для решения проблемы его стоит создать явно. Мы можем использовать его как точку контроля. В момент переключения страницы мы можем принудительно фиксировать состояние всех таблиц формы.
Проанализируем пример использования события ПриСменеСтраницы для панели ГруппаСтраницы:
&НаКлиенте
Процедура ГруппаСтраницыПриСменеСтраницы(Элемент, ТекущаяСтраница)
// Выясним, на какую страницу переходит пользователь
ИмяСтраницы = ТекущаяСтраница.Имя;
// Если на странице есть важная таблица, убедимся, что фокус не потерян
Если ИмяСтраницы = "СтраницаТовары" Тогда
// Принудительно устанавливаем фокус на таблицу, если это необходимо
ТекущийЭлемент = Элементы.Товары;
КонецЕсли;
КонецПроцедуры
Помимо программного кода, обратите внимание на свойства самой таблицы в конфигураторе:
Авто, платформа может пытаться перечитать данные при каждом "появлении" страницы перед глазами пользователя.Отбор у элемента Таблица (для коллекций ДанныеФормыКоллекция), помните, что программная установка отборов на сервере часто сбрасывает текущую строку.Подводя итог, можно сказать, что проблема "прыгающей" строки — это следствие архитектуры управляемого приложения, где интерфейс является лишь отражением данных. Для стабильной работы в толстом клиенте необходимо строго контролировать контекстные серверные вызовы и при необходимости вручную восстанавливать состояние элементов управления, используя идентификаторы строк.