При разработке расширений для типовых конфигураций (например, 1С:УНФ) программисты часто сталкиваются с тем, что после внесения правок стандартное поведение формы меняется. Распространенная проблема: при добавлении новой строки в табличную часть «Запасы» фокус внезапно «улетает» на первую колонку (номер строки), хотя до доработки он корректно устанавливался на номенклатуру. Проанализируем, почему это происходит и как вернуть привычное поведение интерфейса.
ПриАктивизацииСтрокиОсновная причина кроется в архитектуре управляемого приложения. Событие ПриАктивизацииСтроки срабатывает очень часто — при каждом перемещении курсора, при добавлении новой строки, при обновлении списка. Если в коде этого обработчика присутствует вызов серверной процедуры с директивой &НаСервере (контекстный вызов), платформа выполняет следующие действия:
В момент этой перерисовки контекст фокуса сбрасывается. Платформа «забывает», что она только что создала строку и собиралась поставить курсор в колонку Номенклатура, и устанавливает его по умолчанию в начало строки. Важно помнить: в обработчике этого события крайне не рекомендуется использовать серверные методы формы с контекстом.
Чтобы избежать «тяжелой» передачи формы на сервер, мы должны использовать директиву &НаСервереБезКонтекста. В этом случае на сервер передаются только те параметры, которые мы явно указали, и состояние фокуса на клиенте сохраняется. Рассмотрим, как правильно переписать вызов получения цен.
Вместо передачи всей формы, передадим только нужные ссылки:
&НаКлиенте
Процедура Расш1_ЗапасыПриАктивизацииСтрокиПосле(Элемент)
ТекущиеДанные = Элемент.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Вызываем сервер без контекста формы
ДанныеЦен = ПолучитьАктуальныеЦеныНаСервере(ТекущиеДанные.Номенклатура, ТекущиеДанные.Характеристика, Объект.Дата);
// Обновляем элементы инфо-панели на клиенте
Обработка_Остаток = ТекущиеДанные.ОстатокОбщий;
ЗаполнитьЦеныНаФорме(ДанныеЦен);
КонецПроцедуры
При попытке передать результат запроса (например, Таблицу Значений) с сервера на клиент, вы обязательно столкнетесь с ошибкой. Тип ТаблицаЗначений не существует на клиенте. Чтобы передать данные о 5–7 видах цен, как в нашем случае, необходимо преобразовать их в Массив или Структуру.
Проанализируем пример функции, которая корректно возвращает данные:
&НаСервереБезКонтекста
Функция ПолучитьАктуальныеЦеныНаСервере(НоменклатураСсылка, ХарактеристикаСсылка, ДатаДокумента)
МассивЦен = Новый Массив;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ИЗ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Номенклатура...)";
// Установка параметров запроса...
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СтруктураЦены = Новый Структура("ВидЦены, Цена", Выборка.ВидЦены, Выборка.Цена);
МассивЦен.Добавить(СтруктураЦены);
КонецЦикла;
Возврат МассивЦен;
КонецФункции
Если даже после перехода на бесконтекстные вызовы фокус ведет себя нестабильно, мы можем явно указать платформе, куда его установить. За управление фокусом в управляемой форме отвечает свойство ЭтаФорма.ТекущийЭлемент.
Разберем, как это реализовать при добавлении новой строки. В событии ПриИзменении номенклатуры или в обработчике ожидания добавим код:
&НаКлиенте
Процедура УстановитьФокусНаНоменклатуру()
Элементы.Запасы.ТекущийЭлемент = Элементы.ЗапасыНоменклатура;
КонецПроцедуры
Иногда платформа перехватывает управление фокусом уже после того, как наш код отработал. В таких ситуациях поможет «хитрость» с обработчиком ожидания. Это позволяет выполнить код чуть позже, когда все стандартные механизмы добавления строки завершатся.
ПриАктивизацииСтроки подключаем обработчик на 0.1 секунды.
&НаКлиенте
Процедура Расш1_ЗапасыПриАктивизацииСтрокиПосле(Элемент)
// Отключаем старый обработчик, если он еще не сработал
ОтключитьОбработчикОжидания("ВыполнитьОбновлениеПанели");
// Подключаем новый на минимальное время
ПодключитьОбработчикОжидания("ВыполнитьОбновлениеПанели", 0.1, Истина);
КонецПроцедуры
Рассмотрим ситуацию, когда пользователь быстро листает список стрелками. При каждом нажатии будет происходить вызов сервера. Чтобы этого избежать, выясним причину лишних вызовов и внедрим простую проверку на изменение строки:
&НаКлиенте
Перем ПоследнийИдентификаторСтроки; // Переменная модуля
&НаКлиенте
Процедура Расш1_ЗапасыПриАктивизацииСтрокиПосле(Элемент)
ТекущаяСтрока = Элемент.ТекущаяСтрока;
Если ТекущаяСтрока = ПоследнийИдентификаторСтроки Тогда
Возврат; // Строка та же, сервер не дергаем
КонецЕсли;
ПоследнийИдентификаторСтроки = ТекущаяСтрока;
// Далее логика вызова сервера...
КонецПроцедуры
Резюмируем: для сохранения фокуса необходимо исключить директиву &НаСервере из цепочки вызовов ПриАктивизацииСтроки, использовать &НаСервереБезКонтекста и передавать данные в простых типах (Массивы, Структуры). Если стандартная логика все еще мешает, применяем явную установку ТекущийЭлемент через обработчик ожидания.