Как сохранить фокус на ячейке «Номенклатура» при добавлении строки в управляемых формах 1С

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

При разработке расширений для типовых конфигураций (например, 1С:УНФ) программисты часто сталкиваются с тем, что после внесения правок стандартное поведение формы меняется. Распространенная проблема: при добавлении новой строки в табличную часть «Запасы» фокус внезапно «улетает» на первую колонку (номер строки), хотя до доработки он корректно устанавливался на номенклатуру. Проанализируем, почему это происходит и как вернуть привычное поведение интерфейса.

Почему сбивается фокус: разбираем механику события ПриАктивизацииСтроки

Основная причина кроется в архитектуре управляемого приложения. Событие ПриАктивизацииСтроки срабатывает очень часто — при каждом перемещении курсора, при добавлении новой строки, при обновлении списка. Если в коде этого обработчика присутствует вызов серверной процедуры с директивой &НаСервере (контекстный вызов), платформа выполняет следующие действия:

  1. Полностью упаковывает состояние всей формы (все реквизиты, данные табличных частей).
  2. Передает этот массив данных на сервер.
  3. Выполняет серверный код.
  4. Возвращает всё состояние формы обратно на клиент и «перерисовывает» её.

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

Шаг 1. Переход на бесконтекстные серверные вызовы

Чтобы избежать «тяжелой» передачи формы на сервер, мы должны использовать директиву &НаСервереБезКонтекста. В этом случае на сервер передаются только те параметры, которые мы явно указали, и состояние фокуса на клиенте сохраняется. Рассмотрим, как правильно переписать вызов получения цен.

Вместо передачи всей формы, передадим только нужные ссылки:


&НаКлиенте
Процедура Расш1_ЗапасыПриАктивизацииСтрокиПосле(Элемент)
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Вызываем сервер без контекста формы
    ДанныеЦен = ПолучитьАктуальныеЦеныНаСервере(ТекущиеДанные.Номенклатура, ТекущиеДанные.Характеристика, Объект.Дата);
    
    // Обновляем элементы инфо-панели на клиенте
    Обработка_Остаток = ТекущиеДанные.ОстатокОбщий;
    ЗаполнитьЦеныНаФорме(ДанныеЦен);
КонецПроцедуры

Шаг 2. Решение проблемы «Ошибки отображения типов»

При попытке передать результат запроса (например, Таблицу Значений) с сервера на клиент, вы обязательно столкнетесь с ошибкой. Тип ТаблицаЗначений не существует на клиенте. Чтобы передать данные о 5–7 видах цен, как в нашем случае, необходимо преобразовать их в Массив или Структуру.

Проанализируем пример функции, которая корректно возвращает данные:


&НаСервереБезКонтекста
Функция ПолучитьАктуальныеЦеныНаСервере(НоменклатураСсылка, ХарактеристикаСсылка, ДатаДокумента)
    МассивЦен = Новый Массив;
    
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ ... ИЗ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Номенклатура...)";
    // Установка параметров запроса...
    
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        СтруктураЦены = Новый Структура("ВидЦены, Цена", Выборка.ВидЦены, Выборка.Цена);
        МассивЦен.Добавить(СтруктураЦены);
    КонецЦикла;
    
    Возврат МассивЦен; 
КонецФункции

Шаг 3. Принудительная установка фокуса (ТекущийЭлемент)

Если даже после перехода на бесконтекстные вызовы фокус ведет себя нестабильно, мы можем явно указать платформе, куда его установить. За управление фокусом в управляемой форме отвечает свойство ЭтаФорма.ТекущийЭлемент.

Разберем, как это реализовать при добавлении новой строки. В событии ПриИзменении номенклатуры или в обработчике ожидания добавим код:


&НаКлиенте
Процедура УстановитьФокусНаНоменклатуру()
    Элементы.Запасы.ТекущийЭлемент = Элементы.ЗапасыНоменклатура;
КонецПроцедуры

Шаг 4. Оптимизация через обработчик ожидания

Иногда платформа перехватывает управление фокусом уже после того, как наш код отработал. В таких ситуациях поможет «хитрость» с обработчиком ожидания. Это позволяет выполнить код чуть позже, когда все стандартные механизмы добавления строки завершатся.

  1. В событии ПриАктивизацииСтроки подключаем обработчик на 0.1 секунды.
  2. В процедуре обработчика выполняем обновление цен и установку элемента.

&НаКлиенте
Процедура Расш1_ЗапасыПриАктивизацииСтрокиПосле(Элемент)
    // Отключаем старый обработчик, если он еще не сработал
    ОтключитьОбработчикОжидания("ВыполнитьОбновлениеПанели");
    // Подключаем новый на минимальное время
    ПодключитьОбработчикОжидания("ВыполнитьОбновлениеПанели", 0.1, Истина);
КонецПроцедуры

Дополнительные советы по оптимизации

Рассмотрим ситуацию, когда пользователь быстро листает список стрелками. При каждом нажатии будет происходить вызов сервера. Чтобы этого избежать, выясним причину лишних вызовов и внедрим простую проверку на изменение строки:


&НаКлиенте
Перем ПоследнийИдентификаторСтроки; // Переменная модуля

&НаКлиенте
Процедура Расш1_ЗапасыПриАктивизацииСтрокиПосле(Элемент)
    ТекущаяСтрока = Элемент.ТекущаяСтрока;
    Если ТекущаяСтрока = ПоследнийИдентификаторСтроки Тогда
        Возврат; // Строка та же, сервер не дергаем
    КонецЕсли;
    ПоследнийИдентификаторСтроки = ТекущаяСтрока;
    
    // Далее логика вызова сервера...
КонецПроцедуры

Резюмируем: для сохранения фокуса необходимо исключить директиву &НаСервере из цепочки вызовов ПриАктивизацииСтроки, использовать &НаСервереБезКонтекста и передавать данные в простых типах (Массивы, Структуры). Если стандартная логика все еще мешает, применяем явную установку ТекущийЭлемент через обработчик ожидания.

← На главную