Динамический список 1С — основной способ показать в форме данные из базы: он сам читает порции строк, применяет отборы, поиск и сортировку. Но стоит выйти за рамки стандартного поведения — распечатать список, показать один товар в нескольких ячейках, поставить флажки или иконки, — и начинаются вопросы. Ниже пять приемов, которые я регулярно применяю на проектах, с кодом и подводными камнями.

Коротко по сути: печать делается через исполняемую схему и настройки компоновки самого списка; уникальность строк — через ПоляКлюча и ВидКлюча; пометки — через параметр запроса и событие активизации ячейки; иконки — через картинку строки, поле картинки или ПриПолученииДанныхНаСервере; программная правка запроса — через СхемаЗапроса с РежимКомпоновкиДанных = Истина.

Печать динамического списка с отборами и поиском

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

У реквизита типа ДинамическийСписок есть два серверных метода:

МетодЧто возвращает
ПолучитьИсполняемуюСхемуКомпоновкиДанных()Готовую схему компоновки, построенную по запросу списка
ПолучитьИсполняемыеНастройкиКомпоновкиДанных()Настройки со всеми отборами, поиском, видимыми колонками, сортировкой и оформлением

Анализировать текст запроса, основную таблицу и пользовательские настройки вручную больше не нужно.

1С
&НаКлиенте
Процедура ПечатьСписка(Команда)

	ТабличныйДокумент = ПечатьСпискаНаСервере();
	ТабличныйДокумент.Показать(НСтр("ru = 'Список документов'"));

КонецПроцедуры

&НаСервере
Функция ПечатьСпискаНаСервере()

	Схема = Список.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
	Настройки = Список.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();

	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки);

	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);

	ТабличныйДокумент = Новый ТабличныйДокумент;
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
	ПроцессорВывода.УстановитьДокумент(ТабличныйДокумент);
	ПроцессорВывода.Вывести(ПроцессорКомпоновки);

	Возврат ТабличныйДокумент;

КонецФункции

Вместо табличного документа результат можно вывести в таблицу значений (генератор макета для коллекции значений и ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений) — так удобно делать выгрузку в Excel или отправку по API ровно тех строк, что видит пользователь.

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

Динамический список 1С: на печать попадают ровно те строки, что пользователь видит с отборами и поиском
Динамический список 1С: на печать попадают ровно те строки, что пользователь видит с отборами и поиском

Уникальность строк по нескольким полям

Типичная ситуация: в форму подбора номенклатуры добавили колонку «Ячейка хранения» соединением с остатками. Товар лежит в двух ячейках — список перестает строиться с ошибкой о повторении ключевых полей.

Причина: при заполненном свойстве ОсновнаяТаблица ключом строки считается ссылка основной таблицы, и она должна быть уникальной. Решение в современных версиях платформы — составной ключ:

  1. Задать ПоляКлюча — фиксированный массив имен полей, которые вместе уникальны.
  2. Установить ВидКлюча = ВидКлючаДинамическогоСписка.КлючСтроки.
  3. Очистить ОсновнаяТаблица: пока она заполнена, ПоляКлюча игнорируется.
  4. Гарантировать, что ни одно ключевое поле не возвращает NULL или Неопределено. Для левых соединений используйте ЕСТЬNULL с пустой ссылкой.
1С
&НаСервере
Процедура НастроитьСписокСЯчейками()

	СвойстваСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
	СвойстваСписка.ТекстЗапроса = ТекстЗапросаСОстаткамиПоЯчейкам(СписокНоменклатуры.ТекстЗапроса);
	СвойстваСписка.ОсновнаяТаблица = "";
	СвойстваСписка.ДинамическоеСчитываниеДанных = Истина;
	ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.СписокНоменклатуры, СвойстваСписка);

	ПоляКлюча = Новый Массив;
	ПоляКлюча.Добавить("Ссылка");
	ПоляКлюча.Добавить("Ячейка");
	СписокНоменклатуры.ПоляКлюча = Новый ФиксированныйМассив(ПоляКлюча);
	СписокНоменклатуры.ВидКлюча = ВидКлючаДинамическогоСписка.КлючСтроки;

	ПолеЯчейка = Элементы.Добавить("СписокНоменклатурыЯчейка", Тип("ПолеФормы"), Элементы.СписокНоменклатуры);
	ПолеЯчейка.Вид = ВидПоляФормы.ПолеВвода;
	ПолеЯчейка.ПутьКДанным = "СписокНоменклатуры.Ячейка";

	ПолеОстаток = Элементы.Добавить("СписокНоменклатурыОстатокВЯчейке", Тип("ПолеФормы"), Элементы.СписокНоменклатуры);
	ПолеОстаток.Вид = ВидПоляФормы.ПолеВвода;
	ПолеОстаток.ПутьКДанным = "СписокНоменклатуры.ОстатокВЯчейке";
	ПолеОстаток.Заголовок = НСтр("ru = 'Остаток'");

КонецПроцедуры

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

Динамический список 1С: один товар в разных ячейках различается составным ключом
Динамический список 1С: один товар в разных ячейках различается составным ключом

Пометки (флажки) одним кликом

Динамический список только для чтения, поэтому флажок в нем — это вычисляемое поле запроса, а не редактируемый реквизит. Схема:

  1. В запрос добавляется поле Контрагенты.Ссылка В (&ВыбранныеСсылки) КАК Выбран.
  2. Колонка Выбран выводится как флажок или поле картинки.
  3. Отмеченные ссылки хранятся в массиве на клиенте и передаются в параметр списка.
Запрос 1С
ВЫБРАТЬ
	Контрагенты.Ссылка В (&ВыбранныеСсылки) КАК Выбран,
	Контрагенты.Ссылка КАК Ссылка,
	Контрагенты.Наименование КАК Наименование,
	Контрагенты.ИНН КАК ИНН
ИЗ
	Справочник.Контрагенты КАК Контрагенты

Проблема в клике: событие Выбор срабатывает только по двойному щелчку, а ПриАктивизацииЯчейки не срабатывает повторно, если пользователь щелкает по флажку в уже активной ячейке. Хитрость: после каждой обработки переводить фокус на соседнюю колонку справа. Тогда любой щелчок по флажку — это активизация новой ячейки.

1С
&НаКлиенте
Перем ВыбранныеСсылки;

&НаКлиенте
Процедура ПриОткрытии(Отказ)

	ВыбранныеСсылки = Новый Массив;
	Список.Параметры.УстановитьЗначениеПараметра("ВыбранныеСсылки", ВыбранныеСсылки);
	ПеревестиФокусСКолонкиВыбран();

КонецПроцедуры

&НаКлиенте
Процедура СписокПриАктивизацииЯчейки(Элемент)

	Если Элемент.ТекущийЭлемент <> Элементы.СписокВыбран Тогда
		Возврат;
	КонецЕсли;

	ТекущиеДанные = Элемент.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;

	Индекс = ВыбранныеСсылки.Найти(ТекущиеДанные.Ссылка);
	Если Индекс = Неопределено Тогда
		ВыбранныеСсылки.Добавить(ТекущиеДанные.Ссылка);
	Иначе
		ВыбранныеСсылки.Удалить(Индекс);
	КонецЕсли;

	Список.Параметры.УстановитьЗначениеПараметра("ВыбранныеСсылки", ВыбранныеСсылки);
	ПеревестиФокусСКолонкиВыбран();

КонецПроцедуры

// Переводит фокус на первую видимую колонку правее колонки флажка,
// чтобы следующий щелчок по флажку снова вызвал ПриАктивизацииЯчейки.
&НаКлиенте
Процедура ПеревестиФокусСКолонкиВыбран()

	Колонки = Элементы.Список.ПодчиненныеЭлементы;
	Индекс = Колонки.Индекс(Элементы.СписокВыбран);
	Для Номер = Индекс + 1 По Колонки.Количество() - 1 Цикл
		Если Колонки[Номер].Видимость Тогда
			Элементы.Список.ТекущийЭлемент = Колонки[Номер];
			Прервать;
		КонецЕсли;
	КонецЦикла;

КонецПроцедуры

Обработчик СписокПриАктивизацииЯчейки назначьте в свойствах таблицы формы. Колонку Выбран поставьте первой, а для вида используйте поле картинки с картинкой-флажком из библиотеки картинок — так нет «кликабельного» вида у недоступного для редактирования флажка.

Ограничение: каждое изменение параметра перечитывает список с сервера. Для сотен отметок подряд это ощутимо; если пользователю нужно массово отмечать, лучше дать команды «Отметить все по отбору» и «Снять все».

Иконки и индикаторы в строках

ЗадачаИнструмент
Одна иконка в начале строки (пометка удаления, проведен)Свойства таблицы ПутьКДаннымКартинкиСтроки и КартинкаСтрок
Иконка в произвольной колонкеПоле формы вида «Поле картинки» со свойством КартинкаЗначений
Иконка по значению реквизитаВычисляемое поле в запросе (ВЫБОР ... КОГДА)
Иконка по сложному правилуОбработчик ПриПолученииДанныхНаСервере
Цвет текста или фонаУсловное оформление списка

Картинка строки. Свойство ПутьКДаннымКартинкиСтроки у таблицы формы указывает на поле списка. Для стандартного поля СтандартнаяКартинка платформа сама покажет пометку удаления, а у документов — и статус проведения. Если поле числовое, значение трактуется как индекс в коллекции из свойства КартинкаСтрок; если поле типа Картинка — выводится как есть.

Своя коллекция. Общая картинка в виде «ленты» иконок одинакового размера (например, 16×16) превращается в коллекцию с индексами 0, 1, 2… Ее можно указать в КартинкаСтрок или в КартинкаЗначений поля картинки.

Колонка-индикатор кодом. Добавляем вычисляемое поле в запрос и колонку на форму без правки формы в конфигураторе — удобно в расширениях:

1С
&НаСервере
Процедура ДобавитьИндикаторКатегории()

	ТекстПоля = "
	|	ВЫБОР
	|		КОГДА ТипыБилетов.Категория = ЗНАЧЕНИЕ(Перечисление.КатегорииБилетов.Платный) ТОГДА 0
	|		КОГДА ТипыБилетов.Категория = ЗНАЧЕНИЕ(Перечисление.КатегорииБилетов.Бесплатный) ТОГДА 1
	|		КОГДА ТипыБилетов.Категория = ЗНАЧЕНИЕ(Перечисление.КатегорииБилетов.Льготный) ТОГДА 2
	|		ИНАЧЕ 3
	|	КОНЕЦ КАК ИндексКатегории,";

	СвойстваСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
	СвойстваСписка.ОсновнаяТаблица = "Справочник.ТипыБилетов";
	СвойстваСписка.ДинамическоеСчитываниеДанных = Истина;
	СвойстваСписка.ТекстЗапроса = СтрЗаменить(Список.ТекстЗапроса, "ВЫБРАТЬ", "ВЫБРАТЬ" + ТекстПоля);
	ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.Список, СвойстваСписка);

	Колонка = Элементы.Добавить("СписокИндексКатегории", Тип("ПолеФормы"), Элементы.Список);
	Колонка.Вид = ВидПоляФормы.ПолеКартинки;
	Колонка.ПутьКДанным = "Список.ИндексКатегории";
	Колонка.КартинкаЗначений = БиблиотекаКартинок.КоллекцияИндикаторыКатегорий;
	Колонка.ОтображатьВШапке = Ложь;
	Колонка.Ширина = 2;

	Если Элементы.Список.ПодчиненныеЭлементы.Количество() > 1 Тогда
		Элементы.Переместить(Колонка, Элементы.Список, Элементы.Список.ПодчиненныеЭлементы[0]);
	КонецЕсли;

КонецПроцедуры

Здесь ТипыБилетов — псевдоним основной таблицы в запросе вашего списка, а КоллекцияИндикаторыКатегорий — ваша общая картинка-лента. Простая замена ВЫБРАТЬ работает, только если это слово встречается в запросе один раз; для сложных запросов используйте схему запроса (ниже).

Признак по правилу в коде. Обработчик таблицы ПриПолученииДанныхНаСервере получает порцию строк перед отправкой на клиент. Поле должно существовать в запросе (например, 0 КАК ИндексОшибки), а значение мы перезаписываем:

1С
&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)

	Для Каждого КлючИЗначение Из Строки Цикл
		ДанныеСтроки = КлючИЗначение.Значение.Данные;
		ЕстьОшибка = Не ЗначениеЗаполнено(ДанныеСтроки.Категория) Или Не ЗначениеЗаполнено(ДанныеСтроки.Маршрут);
		ДанныеСтроки.ИндексОшибки = ?(ЕстьОшибка, 0, 1);
	КонецЦикла;

КонецПроцедуры

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

Программная правка запроса через СхемаЗапроса

Менять текст запроса строковыми заменами ненадежно, поэтому для сложных правок я использую объект СхемаЗапроса. Но у запросов динамических списков есть фигурные секции компоновки ({ГДЕ ...}, {ВЫБРАТЬ ...}), через которые работают отборы и параметры. Если загрузить такой текст в схему в обычном режиме и выгрузить обратно, секции искажаются: например, подзапрос внутри {ГДЕ} получает псевдоним КАК Поле2, и отбор по параметру тихо пропадает.

Решение — свойство РежимКомпоновкиДанных, установленное до загрузки текста:

1С
&НаСервере
Процедура ИзменитьЗапросСписка()

	СхемаЗапроса = Новый СхемаЗапроса;
	СхемаЗапроса.РежимКомпоновкиДанных = Истина;
	СхемаЗапроса.УстановитьТекстЗапроса(Список.ТекстЗапроса);

	ПоследнийЗапрос = СхемаЗапроса.ПакетЗапросов[СхемаЗапроса.ПакетЗапросов.Количество() - 1];
	// Здесь добавляем поля, соединения, условия через объектную модель схемы.

	СвойстваСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
	СвойстваСписка.ТекстЗапроса = СхемаЗапроса.ПолучитьТекстЗапроса();
	СвойстваСписка.ОсновнаяТаблица = Список.ОсновнаяТаблица;
	СвойстваСписка.ДинамическоеСчитываниеДанных = Истина;
	ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.Список, СвойстваСписка);

КонецПроцедуры

После любой программной правки сравните текст запроса до и после в отладчике: потерянная секция компоновки не дает ошибки, она просто молча отключает отбор.

Типичные ошибки

  • Печать выводит все колонки. Использованы собственные настройки вместо ПолучитьИсполняемыеНастройкиКомпоновкиДанных.
  • «Повторение ключевых полей». Не очищена ОсновнаяТаблица или ключевое поле возвращает NULL.
  • Флажок срабатывает через раз. Фокус остается на колонке флажка после обработки.
  • Иконки не меняются. Поле не добавлено в запрос, а только на форму, либо ошибка в ПутьКДанным.
  • Пропал отбор после доработки. Текст правили через СхемаЗапроса без РежимКомпоновкиДанных.
  • Список тормозит при прокрутке. Тяжелая логика в ПриПолученииДанныхНаСервере.

Доработки форм и списков в типовых конфигурациях я делаю в расширениях, чтобы они переживали обновления. Подробнее — в услугах доработка 1С и расширения 1С.

Коротко

  • Динамический список 1С печатается через его исполняемую схему и настройки — с отборами, поиском и видимыми колонками.
  • Составной ключ строки: ПоляКлюча, ВидКлюча = КлючСтроки, пустая ОсновнаяТаблица, без NULL в ключах.
  • Флажки делаются параметром запроса и переводом фокуса после клика.
  • Иконки: картинка строки, поле картинки, вычисляемое поле или ПриПолученииДанныхНаСервере.
  • Правка запроса через СхемаЗапроса — только с РежимКомпоновкиДанных = Истина.