Как заполнить форму списка справочника новыми значениями ФИО друг под другом в 1С 8.3 на обычных формах

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

При разработке и кастомизации интерфейсов на платформе 1С:Предприятие 8.3 (особенно когда речь идет об обычных формах) разработчики часто сталкиваются с задачей компактного и информативного размещения данных. Классический пример — справочник ФизическиеЛица, где необходимо отобразить фамилию, имя и отчество физического лица в одной колонке списка, расположив их друг под другом (в три строки внутри одной ячейки), чтобы сэкономить горизонтальное пространство экрана.

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

Архитектурная особенность обычных форм: почему нельзя «записать» данные в список

Для начала выясним причину, по которой у автора темы возникла сложность при попытке программной записи данных в форму списка. В обычных формах элемент управления ТабличноеПоле, связанный с типом значения СправочникСписок, работает как динамический считыватель данных напрямую из базы данных.

Важно понимать: форма списка не хранит данные внутри себя. Она является лишь «окном» для отображения информации, которая уже физически записана в таблицах СУБД. Попытка напрямую присвоить значение ячейке строки списка в обход базы данных не увенчается успехом, поскольку при первой же прокрутке (скроллинге) или обновлении списка платформа заново перечитает данные из БД, стерев все ваши программные изменения. Таким образом, чтобы данные появились в списке, они должны быть либо физически сохранены в реквизитах справочника, либо рассчитаны динамически «на лету» при выводе строк на экран.

Рассмотрим два основных сценария решения этой задачи в зависимости от того, как хранятся ваши данные.

Решение 1: Статическая привязка реквизитов через свойства колонок (без кода)

Если Фамилия, Имя и Отчество являются реальными физическими реквизитами справочника (то есть в метаданных справочника созданы соответствующие реквизиты Фамилия, Имя, Отчество с типом Строка), то для их красивого отображения друг под другом нам не потребуется писать ни одной строки кода. Все настраивается декларативно в конфигураторе.

Разберем этот процесс по шагам:

  1. Откройте форму списка справочника в конфигураторе.
  2. Найдите на форме основное табличное поле (обычно оно имеет имя СправочникСписок) и откройте его свойства.
  3. Нажмите правой кнопкой мыши по табличному полю и выберите пункт меню «Размещение данных...». В открывшемся окне установите флажки напротив реквизитов Фамилия, Имя и Отчество. Нажмите «ОК». На форме появятся три новые колонки.
  4. Теперь нам нужно сгруппировать эти колонки так, чтобы они располагались вертикально в ряд. Для этого откройте свойства добавленных колонок.
  5. Выделите колонку «Фамилия». В палитре свойств найдите категорию «Макет» и свойство Положение. Установите его в значение Новая колонка. В свойстве Данные должен быть указан реквизит Фамилия.
  6. Выделите колонку «Имя». Для нее свойство Положение установите в значение На следующей строке. В поле Данные укажите реквизит Имя.
  7. Выделите колонку «Отчество». Свойство Положение установите в значение На следующей строке (или В той же колонке, если вы хотите объединить их по-другому). В поле Данные укажите реквизит Отчество.

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

Решение 2: Динамическое заполнение виртуальных колонок через код

А что делать, если Фамилия, Имя и Отчество не хранятся в самом справочнике как реквизиты? Например, в типовых конфигурациях (таких как «Зарплата и Управление Персоналом») ФИО физических лиц хранится в периодическом регистре сведений ФИОФизическихЛиц, чтобы система могла отслеживать историю смены фамилий. В этом случае связать колонки напрямую через свойство Данные не получится.

Для решения этой задачи нам потребуется создать на форме списка три «виртуальные» (не связанные с данными напрямую) колонки и заполнять их программно.

Рассмотрим два метода реализации этого подхода и проанализируем их эффективность.

Метод А: Использование события ПриВыводеСтроки (Простой, но медленный)

Событие ПриВыводеСтроки вызывается платформой для каждой строки табличного поля при ее отрисовке на экране. Мы можем перехватить это событие и заполнить ячейки нужным текстом.

Посмотрим на пример реализации:


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

Внимание! Этот метод обладает критическим недостатком. Если пользователь начнет быстро прокручивать список, система будет выполнять отдельный SQL-запрос к базе данных для каждой выводящейся строки (так называемая проблема N+1 запроса). Это приведет к сильным зависаниям интерфейса («фризам») и высокой нагрузке на сервер.

Метод Б: Использование события ПриПолученииДанных (Рекомендуемый высокопроизводительный способ)

Чтобы избежать проблем с производительностью, мы должны использовать событие ПриПолученииДанных. Оно отрабатывает пакетно для всех видимых на экране строк одновременно (обычно около 20–30 строк). Это позволяет нам собрать все ссылки отображаемых элементов, выполнить один-единственный быстрый запрос к базе данных и распределить полученные результаты по строкам.

Разберем по шагам правильную программную реализацию:


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

Использование этого метода гарантирует мгновенную работу интерфейса и плавную прокрутку списка физических лиц даже при работе со списками из сотен тысяч записей.

Решение проблемы обновления списка при записи нового элемента

Иногда возникает ситуация, когда при создании нового элемента (например, программно из внешней обработки или из другой формы) новые данные не сразу отображаются в открытой форме списка. Чтобы заставить систему принудительно обновить список и перерисовать интерфейс, используются специальные методы оповещения.

Если вы создаете элемент программно и хотите, чтобы форма списка мгновенно узнала об этом и обновилась, вызовите метод глобального контекста ОповеститьОбИзменении:


// Создаем и записываем новое физлицо в коде внешней обработки
НовоеФизЛицо = Справочники.ФизическиеЛица.СоздатьЭлемент();
НовоеФизЛицо.Наименование = "Иванов Иван Иванович";
НовоеФизЛицо.Фамилия = "Иванов";
НовоеФизЛицо.Имя = "Иван";
НовоеФизЛицо.Отчество = "Иванович";
НовоеФизЛицо.Записать();

// Посылаем платформе сигнал о том, что данные справочника изменились
ОповеститьОбИзменении(НовоеФизЛицо.Ссылка);

Если вам нужно обновить строки списка изнутри программного кода самой формы списка, вы можете воспользоваться методами обновления табличного поля:


// Вариант 1: Принудительное обновление строк в текущей видимой области
ЭлементыФормы.СправочникСписок.ОбновитьСтроки();

// Вариант 2: Полный перезапуск считывания данных из базы данных
ЭлементыФормы.СправочникСписок.Значение.Обновить();

Применение этих методов гарантирует актуальность выводимых на экран данных в любой момент времени.

← На главную